mirror of
https://github.com/libretro/pcsx2.git
synced 2024-12-03 15:41:01 +00:00
Portaudio: sync with portaudio /trunk up to revision 1505 (mostly linux stuff).
---- Revision 1505: wasapi: - implemented support of non-Interleaved buffers (paNonInterleaved) for WASAPI blocking interface for input and output - blocking methods will now use PA sample converters Revision 1504: alsa: - releasing memory of non-MMAPed buffer on stream closure by Pa_CloseStream Revision 1503: alsa: - reverted buffer size (2048) hardcoding for non-MMAPed devices to avoid crash on wrong buffer size usage (whole area requires more work) - optimized non-MMAPed device operation to avoid malloc(memset)/free usage on every processing call avoiding significant performance penalty Revision 1502: alsa: - fixed deadlock in PaAlsaStream_WaitForFrames if device is paused, poll() results are now checked for 0 and if 64 times exceeded an error (paTimedOut) is returned - removed hardcoded low-limit of latency for non-MMAPed devices, it is an obligation for user to set an acceptable/desired latency value - tuned XRUN recovery sequence for MMAPed devices Revision 1501: fixed compile for DirectSound implementation under MSYS, missing DSSPEAKER_7POINT1_SURROUND define git-svn-id: http://pcsx2.googlecode.com/svn/trunk@3133 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
644d4cda7f
commit
5a97b9dd08
142
3rdparty/portaudio/src/hostapi/alsa/pa_linux_alsa.c
vendored
142
3rdparty/portaudio/src/hostapi/alsa/pa_linux_alsa.c
vendored
@ -122,6 +122,7 @@ typedef struct
|
||||
int userInterleaved, hostInterleaved;
|
||||
int canMmap;
|
||||
void *nonMmapBuffer;
|
||||
unsigned int nonMmapBufferSize;
|
||||
PaDeviceIndex device; /* Keep the device index */
|
||||
|
||||
snd_pcm_t *pcm;
|
||||
@ -809,10 +810,8 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi )
|
||||
|
||||
if( predefined )
|
||||
{
|
||||
hwDevInfos[numDeviceNames - 1].hasPlayback =
|
||||
predefined->hasPlayback;
|
||||
hwDevInfos[numDeviceNames - 1].hasCapture =
|
||||
predefined->hasCapture;
|
||||
hwDevInfos[numDeviceNames - 1].hasPlayback = predefined->hasPlayback;
|
||||
hwDevInfos[numDeviceNames - 1].hasCapture = predefined->hasCapture;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1191,6 +1190,7 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa
|
||||
self->streamDir = streamDir;
|
||||
self->canMmap = 0;
|
||||
self->nonMmapBuffer = NULL;
|
||||
self->nonMmapBufferSize = 0;
|
||||
|
||||
if( !callbackMode && !self->userInterleaved )
|
||||
{
|
||||
@ -1233,7 +1233,6 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se
|
||||
|
||||
PaError result = paNoError;
|
||||
snd_pcm_access_t accessMode, alternateAccessMode;
|
||||
snd_pcm_access_t rwAccessMode, alternateRwAccessMode;
|
||||
int dir = 0;
|
||||
snd_pcm_t *pcm = self->pcm;
|
||||
double sr = *sampleRate;
|
||||
@ -1253,41 +1252,46 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se
|
||||
if( self->userInterleaved )
|
||||
{
|
||||
accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
|
||||
rwAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
|
||||
alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
|
||||
alternateRwAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
|
||||
|
||||
/* test if MMAP supported */
|
||||
self->canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ||
|
||||
snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0;
|
||||
if (!self->canMmap)
|
||||
{
|
||||
accessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
|
||||
alternateAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
|
||||
rwAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
|
||||
alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
|
||||
alternateRwAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
|
||||
|
||||
/* test if MMAP supported */
|
||||
self->canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, accessMode ) >= 0 ||
|
||||
snd_pcm_hw_params_test_access( pcm, hwParams, alternateAccessMode ) >= 0;
|
||||
if (!self->canMmap)
|
||||
{
|
||||
accessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
|
||||
alternateAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
|
||||
}
|
||||
}
|
||||
PA_DEBUG(("%s: device can MMAP: %s\n", __FUNCTION__, (self->canMmap ? "YES" : "NO")));
|
||||
|
||||
/* If requested access mode fails, try alternate mode */
|
||||
self->canMmap = 1;
|
||||
if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
|
||||
{
|
||||
if( snd_pcm_hw_params_set_access( pcm, hwParams, rwAccessMode ) >= 0 )
|
||||
self->canMmap = 0;
|
||||
else
|
||||
{
|
||||
if( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ) < 0 )
|
||||
{
|
||||
int err = 0;
|
||||
if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateRwAccessMode )) >= 0)
|
||||
self->canMmap = 0;
|
||||
else
|
||||
if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0)
|
||||
{
|
||||
result = paUnanticipatedHostError;
|
||||
PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
/* Flip mode */
|
||||
self->hostInterleaved = !self->userInterleaved;
|
||||
}
|
||||
}
|
||||
|
||||
ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
|
||||
|
||||
@ -2028,6 +2032,9 @@ static PaError CloseStream( PaStream* s )
|
||||
PaError result = paNoError;
|
||||
PaAlsaStream *stream = (PaAlsaStream*)s;
|
||||
|
||||
free(stream->playback.nonMmapBuffer);
|
||||
free(stream->capture.nonMmapBuffer);
|
||||
|
||||
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
|
||||
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
|
||||
|
||||
@ -2400,7 +2407,7 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
|
||||
snd_pcm_status_t *st;
|
||||
PaTime now = PaUtil_GetTime();
|
||||
snd_timestamp_t t;
|
||||
int errplayback = 0, errcapture = 0;
|
||||
int restartAlsa = 0; /* do not restart Alsa by default */
|
||||
|
||||
snd_pcm_status_alloca( &st );
|
||||
|
||||
@ -2411,7 +2418,17 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
|
||||
{
|
||||
snd_pcm_status_get_trigger_tstamp( st, &t );
|
||||
self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
|
||||
errplayback = snd_pcm_recover( self->playback.pcm, -EPIPE, 0 );
|
||||
|
||||
if (!self->playback.canMmap)
|
||||
{
|
||||
if (snd_pcm_recover( self->playback.pcm, -EPIPE, 0 ) < 0)
|
||||
{
|
||||
PA_DEBUG(( "%s: [playback] non-MMAP-PCM failed recovering from XRUN, will restart Alsa\n", __FUNCTION__ ));
|
||||
++ restartAlsa; /* did not manage to recover */
|
||||
}
|
||||
}
|
||||
else
|
||||
++ restartAlsa; /* always restart MMAPed device */
|
||||
}
|
||||
}
|
||||
if( self->capture.pcm )
|
||||
@ -2421,12 +2438,25 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
|
||||
{
|
||||
snd_pcm_status_get_trigger_tstamp( st, &t );
|
||||
self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
|
||||
errcapture = snd_pcm_recover( self->capture.pcm, -EPIPE, 0 );
|
||||
|
||||
if (!self->capture.canMmap)
|
||||
{
|
||||
if (snd_pcm_recover( self->capture.pcm, -EPIPE, 0 ) < 0)
|
||||
{
|
||||
PA_DEBUG(( "%s: [capture] non-MMAP-PCM failed recovering from XRUN, will restart Alsa\n", __FUNCTION__ ));
|
||||
++ restartAlsa; /* did not manage to recover */
|
||||
}
|
||||
}
|
||||
else
|
||||
++ restartAlsa; /* always restart MMAPed device */
|
||||
}
|
||||
}
|
||||
|
||||
if( errplayback || errcapture )
|
||||
if( restartAlsa )
|
||||
{
|
||||
PA_DEBUG(( "%s: restarting Alsa to recover from XRUN\n", __FUNCTION__ ));
|
||||
PA_ENSURE( AlsaRestart( self ) );
|
||||
}
|
||||
|
||||
end:
|
||||
return result;
|
||||
@ -2609,8 +2639,10 @@ static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self,
|
||||
res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
|
||||
else
|
||||
{
|
||||
/* using realloc for optimisation
|
||||
free( self->nonMmapBuffer );
|
||||
self->nonMmapBuffer = NULL;
|
||||
*/
|
||||
}
|
||||
|
||||
if( res == -EPIPE || res == -ESTRPIPE )
|
||||
@ -2783,6 +2815,12 @@ static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, st
|
||||
{
|
||||
*xrun = 1;
|
||||
}
|
||||
else
|
||||
if( revents & POLLHUP )
|
||||
{
|
||||
*xrun = 1;
|
||||
PA_DEBUG(( "%s: revents has POLLHUP, processing as XRUN\n", __FUNCTION__ ));
|
||||
}
|
||||
else
|
||||
self->ready = 1;
|
||||
|
||||
@ -2865,7 +2903,8 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr
|
||||
PaError result = paNoError;
|
||||
int pollPlayback = self->playback.pcm != NULL, pollCapture = self->capture.pcm != NULL;
|
||||
int pollTimeout = self->pollTimeout;
|
||||
int xrun = 0;
|
||||
int xrun = 0, timeouts = 0;
|
||||
int pollResults;
|
||||
|
||||
assert( self );
|
||||
assert( framesAvail );
|
||||
@ -2912,18 +2951,51 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr
|
||||
totalFds += self->playback.nfds;
|
||||
}
|
||||
|
||||
if( poll( self->pfds, totalFds, pollTimeout ) < 0 )
|
||||
pollResults = poll( self->pfds, totalFds, pollTimeout );
|
||||
|
||||
if( pollResults < 0 )
|
||||
{
|
||||
/* XXX: Depend on preprocessor condition? */
|
||||
if( errno == EINTR )
|
||||
{
|
||||
/* gdb */
|
||||
Pa_Sleep( 1 ); /* avoid hot loop */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* TODO: Add macro for checking system calls */
|
||||
PA_ENSURE( paInternalError );
|
||||
}
|
||||
else
|
||||
if (pollResults == 0)
|
||||
{
|
||||
|
||||
/* Suspended, paused or failed device can provide 0 poll results. To avoid deadloop in such situation
|
||||
* we simply run counter 'timeouts' which detects 0 poll result and accumulates. As soon as 64 timouts
|
||||
* are achieved we simply fail function with paTimedOut to notify waiting methods that device is not capable
|
||||
* of providing audio data anymore and needs some corresponding recovery action.
|
||||
* Note that 'timeouts' is reset to 0 if poll() managed to return non 0 results.
|
||||
*/
|
||||
|
||||
/*PA_DEBUG(( "%s: poll == 0 results, timed out, %d times left\n", __FUNCTION__, 64 - timeouts ));*/
|
||||
|
||||
++ timeouts;
|
||||
if (timeouts > 1) /* sometimes device times out, but normally once, so we do not sleep any time */
|
||||
{
|
||||
Pa_Sleep( 1 ); /* avoid hot loop */
|
||||
}
|
||||
/* not else ! */
|
||||
if (timeouts >= 64) /* audio device not working, shall return error to notify waiters */
|
||||
{
|
||||
PA_DEBUG(( "%s: poll timed out, returning error\n", __FUNCTION__, timeouts ));
|
||||
PA_ENSURE( paTimedOut );
|
||||
}
|
||||
}
|
||||
else
|
||||
if (pollResults > 0)
|
||||
{
|
||||
/* reset timouts counter */
|
||||
timeouts = 0;
|
||||
|
||||
/* check the return status of our pfds */
|
||||
if( pollCapture )
|
||||
@ -2938,6 +3010,7 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* @concern FullDuplex If only one of two pcms is ready we may want to compromise between the two.
|
||||
* If there is less than half a period's worth of samples left of frames in the other pcm's buffer we will
|
||||
@ -3040,8 +3113,20 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se
|
||||
}
|
||||
else
|
||||
{
|
||||
/* using realloc for optimisation
|
||||
free( self->nonMmapBuffer );
|
||||
self->nonMmapBuffer = calloc( self->numHostChannels, snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 ) );
|
||||
*/
|
||||
unsigned int bufferSize = self->numHostChannels * snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 );
|
||||
if (bufferSize > self->nonMmapBufferSize)
|
||||
{
|
||||
self->nonMmapBuffer = realloc(self->nonMmapBuffer, (self->nonMmapBufferSize = bufferSize));
|
||||
if (!self->nonMmapBuffer)
|
||||
{
|
||||
result = paInsufficientMemory;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( self->hostInterleaved )
|
||||
@ -3100,8 +3185,11 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se
|
||||
{
|
||||
*xrun = 1;
|
||||
*numFrames = 0;
|
||||
|
||||
/* using realloc for optimisation
|
||||
free( self->nonMmapBuffer );
|
||||
self->nonMmapBuffer = NULL;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -759,6 +759,9 @@ static PaError AddOutputDeviceInfoFromDirectSound(
|
||||
case DSSPEAKER_SURROUND: count = 4; break;
|
||||
case DSSPEAKER_5POINT1: count = 6; break;
|
||||
case DSSPEAKER_7POINT1: count = 8; break;
|
||||
#ifndef DSSPEAKER_7POINT1_SURROUND
|
||||
#define DSSPEAKER_7POINT1_SURROUND 0x00000008
|
||||
#endif
|
||||
case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
|
||||
#ifndef DSSPEAKER_5POINT1_SURROUND
|
||||
#define DSSPEAKER_5POINT1_SURROUND 0x00000009
|
||||
|
@ -58,6 +58,10 @@
|
||||
#include <functiondiscoverykeys.h>
|
||||
#undef INITGUID
|
||||
#endif
|
||||
#ifndef __MWERKS__
|
||||
#include <malloc.h>
|
||||
#include <memory.h>
|
||||
#endif /* __MWERKS__ */
|
||||
|
||||
#include "pa_util.h"
|
||||
#include "pa_allocation.h"
|
||||
@ -2741,10 +2745,10 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames )
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
UINT32 frames;
|
||||
BYTE *buffer = (BYTE *)_buffer;
|
||||
BYTE *data = NULL;
|
||||
BYTE *user_buffer = (BYTE *)_buffer;
|
||||
BYTE *wasapi_buffer = NULL;
|
||||
DWORD flags = 0;
|
||||
UINT32 buffer_size;
|
||||
UINT32 i;
|
||||
|
||||
// validate
|
||||
if (!stream->running)
|
||||
@ -2755,10 +2759,24 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames )
|
||||
// Notify blocking op has begun
|
||||
ResetEvent(stream->hBlockingOpStreamRD);
|
||||
|
||||
// make a local copy of the user buffer pointer(s), this is necessary
|
||||
// because PaUtil_CopyOutput() advances these pointers every time it is called
|
||||
if (!stream->bufferProcessor.userInputIsInterleaved)
|
||||
{
|
||||
user_buffer = (BYTE *)alloca(sizeof(BYTE *) * stream->bufferProcessor.inputChannelCount);
|
||||
if (user_buffer == NULL)
|
||||
return paInsufficientMemory;
|
||||
|
||||
for (i = 0; i < stream->bufferProcessor.inputChannelCount; ++i)
|
||||
((BYTE **)user_buffer)[i] = ((BYTE **)_buffer)[i];
|
||||
}
|
||||
|
||||
while (_frames != 0)
|
||||
{
|
||||
UINT32 processed, processed_size;
|
||||
|
||||
// Get the available data in the shared buffer.
|
||||
if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &data, &frames, &flags, NULL, NULL)) != S_OK)
|
||||
if ((hr = IAudioCaptureClient_GetBuffer(stream->cclient, &wasapi_buffer, &frames, &flags, NULL, NULL)) != S_OK)
|
||||
{
|
||||
if (hr == AUDCLNT_S_BUFFER_EMPTY)
|
||||
{
|
||||
@ -2779,19 +2797,35 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long _frames )
|
||||
if (frames > _frames)
|
||||
frames = _frames;
|
||||
|
||||
// Copy
|
||||
buffer_size = frames * stream->in.wavex.Format.nBlockAlign;
|
||||
memcpy(buffer, data, buffer_size);
|
||||
buffer += buffer_size;
|
||||
// Register available frames to processor
|
||||
PaUtil_SetInputFrameCount(&stream->bufferProcessor, frames);
|
||||
|
||||
// Register host buffer pointer to processor
|
||||
PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.inputChannelCount);
|
||||
|
||||
// Copy user data to host buffer (with conversion if applicable)
|
||||
processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, frames);
|
||||
|
||||
// Release buffer
|
||||
if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, frames)) != S_OK)
|
||||
// Advance user buffer to consumed portion
|
||||
processed_size = processed * stream->in.wavex.Format.nBlockAlign;
|
||||
if (stream->bufferProcessor.userInputIsInterleaved)
|
||||
{
|
||||
user_buffer += processed_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < stream->bufferProcessor.inputChannelCount; ++i)
|
||||
((BYTE **)user_buffer)[i] = ((BYTE **)user_buffer)[i] + processed_size;
|
||||
}
|
||||
|
||||
// Release host buffer
|
||||
if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->cclient, processed)) != S_OK)
|
||||
{
|
||||
LogHostError(hr);
|
||||
goto stream_rd_end;
|
||||
}
|
||||
|
||||
_frames -= frames;
|
||||
_frames -= processed;
|
||||
}
|
||||
|
||||
stream_rd_end:
|
||||
@ -2808,8 +2842,8 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _fra
|
||||
PaWasapiStream *stream = (PaWasapiStream*)s;
|
||||
|
||||
UINT32 frames;
|
||||
const BYTE *buffer = (BYTE *)_buffer;
|
||||
BYTE *data;
|
||||
const BYTE *user_buffer = (const BYTE *)_buffer;
|
||||
BYTE *wasapi_buffer;
|
||||
HRESULT hr = S_OK;
|
||||
UINT32 next_rev_sleep, blocks, block_sleep_ms;
|
||||
UINT32 i;
|
||||
@ -2854,11 +2888,22 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _fra
|
||||
Sleep(stream->out.prevSleep);
|
||||
stream->out.prevSleep = next_rev_sleep;
|
||||
|
||||
// make a local copy of the user buffer pointer(s), this is necessary
|
||||
// because PaUtil_CopyOutput() advances these pointers every time it is called
|
||||
if (!stream->bufferProcessor.userOutputIsInterleaved)
|
||||
{
|
||||
user_buffer = (const BYTE *)alloca(sizeof(const BYTE *) * stream->bufferProcessor.outputChannelCount);
|
||||
if (user_buffer == NULL)
|
||||
return paInsufficientMemory;
|
||||
|
||||
for (i = 0; i < stream->bufferProcessor.outputChannelCount; ++i)
|
||||
((const BYTE **)user_buffer)[i] = ((const BYTE **)_buffer)[i];
|
||||
}
|
||||
|
||||
// Feed engine
|
||||
for (i = 0; i < blocks; ++i)
|
||||
{
|
||||
UINT32 available;
|
||||
UINT32 buffer_size;
|
||||
UINT32 available, processed;
|
||||
|
||||
// Get block frames
|
||||
frames = stream->out.framesPerHostCallback;
|
||||
@ -2871,6 +2916,7 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _fra
|
||||
while (frames != 0)
|
||||
{
|
||||
UINT32 padding = 0;
|
||||
UINT32 processed_size;
|
||||
|
||||
// Check if blocking call must be interrupted
|
||||
if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
|
||||
@ -2884,14 +2930,14 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _fra
|
||||
goto stream_wr_end;
|
||||
}
|
||||
|
||||
// Get frames available
|
||||
// Calculate frames available
|
||||
if (frames >= padding)
|
||||
available = frames - padding;
|
||||
else
|
||||
available = frames;
|
||||
|
||||
// Get buffer
|
||||
if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, available, &data)) != S_OK)
|
||||
// Get pointer to host buffer
|
||||
if ((hr = IAudioRenderClient_GetBuffer(stream->rclient, available, &wasapi_buffer)) != S_OK)
|
||||
{
|
||||
// Buffer size is too big, waiting
|
||||
if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
|
||||
@ -2900,19 +2946,36 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long _fra
|
||||
goto stream_wr_end;
|
||||
}
|
||||
|
||||
// Copy
|
||||
buffer_size = available * stream->out.wavex.Format.nBlockAlign;
|
||||
memcpy(data, buffer, buffer_size);
|
||||
buffer += buffer_size;
|
||||
// Register available frames to processor
|
||||
PaUtil_SetOutputFrameCount(&stream->bufferProcessor, available);
|
||||
|
||||
// Register host buffer pointer to processor
|
||||
PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.outputChannelCount);
|
||||
|
||||
// Copy user data to host buffer (with conversion if applicable)
|
||||
processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, available);
|
||||
|
||||
// Release buffer
|
||||
if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, available, 0)) != S_OK)
|
||||
// Advance user buffer to consumed portion
|
||||
processed_size = processed * stream->out.wavex.Format.nBlockAlign;
|
||||
if (stream->bufferProcessor.userOutputIsInterleaved)
|
||||
{
|
||||
user_buffer += processed_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < stream->bufferProcessor.outputChannelCount; ++i)
|
||||
((const BYTE **)user_buffer)[i] = ((const BYTE **)user_buffer)[i] + processed_size;
|
||||
}
|
||||
|
||||
// Release host buffer
|
||||
if ((hr = IAudioRenderClient_ReleaseBuffer(stream->rclient, processed, 0)) != S_OK)
|
||||
{
|
||||
LogHostError(hr);
|
||||
goto stream_wr_end;
|
||||
}
|
||||
|
||||
frames -= available;
|
||||
// Deduct frames
|
||||
frames -= processed;
|
||||
}
|
||||
|
||||
_frames -= frames;
|
||||
|
Loading…
Reference in New Issue
Block a user