winealsa.drv: Don't try to control ALSA's behavior.
Now, winealsa maintains its own buffer, which is written to ALSA on the period cycle requested by the application. We also let ALSA start when it has enough data and stop when it runs out, recovering from the expected underruns. This seems to be more like how ALSA expects to be used.
This commit is contained in:
parent
14eaa18dae
commit
b0652dd8bd
|
@ -50,8 +50,8 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
|
||||||
|
|
||||||
#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
|
#define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
|
||||||
|
|
||||||
static const REFERENCE_TIME DefaultPeriod = 200000;
|
static const REFERENCE_TIME DefaultPeriod = 100000;
|
||||||
static const REFERENCE_TIME MinimumPeriod = 100000;
|
static const REFERENCE_TIME MinimumPeriod = 50000;
|
||||||
|
|
||||||
struct ACImpl;
|
struct ACImpl;
|
||||||
typedef struct ACImpl ACImpl;
|
typedef struct ACImpl ACImpl;
|
||||||
|
@ -94,7 +94,7 @@ struct ACImpl {
|
||||||
LONG ref;
|
LONG ref;
|
||||||
|
|
||||||
snd_pcm_t *pcm_handle;
|
snd_pcm_t *pcm_handle;
|
||||||
snd_pcm_uframes_t period_alsa, bufsize_alsa;
|
snd_pcm_uframes_t alsa_bufsize_frames;
|
||||||
snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
|
snd_pcm_hw_params_t *hw_params; /* does not hold state between calls */
|
||||||
snd_pcm_format_t alsa_format;
|
snd_pcm_format_t alsa_format;
|
||||||
|
|
||||||
|
@ -108,8 +108,9 @@ struct ACImpl {
|
||||||
float *vols;
|
float *vols;
|
||||||
|
|
||||||
BOOL initted, started;
|
BOOL initted, started;
|
||||||
|
REFERENCE_TIME mmdev_period_rt;
|
||||||
UINT64 written_frames;
|
UINT64 written_frames;
|
||||||
UINT32 bufsize_frames, held_frames, tmp_buffer_frames, period_us;
|
UINT32 bufsize_frames, held_frames, tmp_buffer_frames;
|
||||||
UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
|
UINT32 lcl_offs_frames; /* offs into local_buffer where valid data starts */
|
||||||
|
|
||||||
HANDLE timer;
|
HANDLE timer;
|
||||||
|
@ -235,49 +236,6 @@ int WINAPI AUDDRV_GetPriority(void)
|
||||||
return Priority_Neutral;
|
return Priority_Neutral;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
|
||||||
* wine_snd_pcm_recover [internal]
|
|
||||||
*
|
|
||||||
* Code slightly modified from alsa-lib v1.0.23 snd_pcm_recover implementation.
|
|
||||||
* used to recover from XRUN errors (buffer underflow/overflow)
|
|
||||||
*/
|
|
||||||
static int wine_snd_pcm_recover(snd_pcm_t *pcm, int err, int silent)
|
|
||||||
{
|
|
||||||
if (err > 0)
|
|
||||||
err = -err;
|
|
||||||
if (err == -EINTR) /* nothing to do, continue */
|
|
||||||
return 0;
|
|
||||||
if (err == -EPIPE) {
|
|
||||||
const char *s;
|
|
||||||
if (snd_pcm_stream(pcm) == SND_PCM_STREAM_PLAYBACK)
|
|
||||||
s = "underrun";
|
|
||||||
else
|
|
||||||
s = "overrun";
|
|
||||||
if (!silent)
|
|
||||||
ERR("%s occurred\n", s);
|
|
||||||
err = snd_pcm_prepare(pcm);
|
|
||||||
if (err < 0) {
|
|
||||||
ERR("cannot recover from %s, prepare failed: %s\n", s, snd_strerror(err));
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (err == -ESTRPIPE) {
|
|
||||||
while ((err = snd_pcm_resume(pcm)) == -EAGAIN)
|
|
||||||
/* wait until suspend flag is released */
|
|
||||||
poll(NULL, 0, 1000);
|
|
||||||
if (err < 0) {
|
|
||||||
err = snd_pcm_prepare(pcm);
|
|
||||||
if (err < 0) {
|
|
||||||
ERR("cannot recover from suspend, prepare failed: %s\n", snd_strerror(err));
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
|
static BOOL alsa_try_open(const char *devnode, snd_pcm_stream_t stream)
|
||||||
{
|
{
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
|
@ -836,9 +794,9 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
|
||||||
ACImpl *This = impl_from_IAudioClient(iface);
|
ACImpl *This = impl_from_IAudioClient(iface);
|
||||||
snd_pcm_sw_params_t *sw_params = NULL;
|
snd_pcm_sw_params_t *sw_params = NULL;
|
||||||
snd_pcm_format_t format;
|
snd_pcm_format_t format;
|
||||||
snd_pcm_uframes_t boundary;
|
snd_pcm_uframes_t alsa_period_frames;
|
||||||
const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
|
const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
|
||||||
unsigned int time_us, rate;
|
unsigned int rate, mmdev_period_frames, alsa_period_us;
|
||||||
int err, i;
|
int err, i;
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
|
@ -863,6 +821,9 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
|
||||||
return E_INVALIDARG;
|
return E_INVALIDARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!duration)
|
||||||
|
duration = 300000; /* 0.03s */
|
||||||
|
|
||||||
EnterCriticalSection(&This->lock);
|
EnterCriticalSection(&This->lock);
|
||||||
|
|
||||||
if(This->initted){
|
if(This->initted){
|
||||||
|
@ -946,10 +907,10 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
time_us = MinimumPeriod / 10;
|
alsa_period_us = duration / 100; /* duration / 10 converted to us */
|
||||||
if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
|
if((err = snd_pcm_hw_params_set_period_time_near(This->pcm_handle,
|
||||||
This->hw_params, &time_us, NULL)) < 0){
|
This->hw_params, &alsa_period_us, NULL)) < 0){
|
||||||
WARN("Unable to set max period time to %u: %d (%s)\n", time_us,
|
WARN("Unable to set period time near %u: %d (%s)\n", alsa_period_us,
|
||||||
err, snd_strerror(err));
|
err, snd_strerror(err));
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
goto exit;
|
goto exit;
|
||||||
|
@ -961,6 +922,27 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if((err = snd_pcm_hw_params_current(This->pcm_handle, This->hw_params)) < 0){
|
||||||
|
WARN("Unable to get current hw params: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
hr = E_FAIL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
|
||||||
|
&alsa_period_frames, NULL)) < 0){
|
||||||
|
WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
hr = E_FAIL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
TRACE("alsa_period_frames: %lu\n", alsa_period_frames);
|
||||||
|
|
||||||
|
if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
|
||||||
|
&This->alsa_bufsize_frames)) < 0){
|
||||||
|
WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
hr = E_FAIL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
sw_params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_sw_params_sizeof());
|
sw_params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_sw_params_sizeof());
|
||||||
if(!sw_params){
|
if(!sw_params){
|
||||||
hr = E_OUTOFMEMORY;
|
hr = E_OUTOFMEMORY;
|
||||||
|
@ -973,8 +955,46 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!duration)
|
if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
|
||||||
duration = 300000; /* 0.03s */
|
sw_params, 1)) < 0){
|
||||||
|
WARN("Unable set start threshold to 0: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
hr = E_FAIL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
|
||||||
|
sw_params, This->alsa_bufsize_frames)) < 0){
|
||||||
|
WARN("Unable set stop threshold to %lu: %d (%s)\n",
|
||||||
|
This->alsa_bufsize_frames, err, snd_strerror(err));
|
||||||
|
hr = E_FAIL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
|
||||||
|
WARN("Unable set sw params: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
hr = E_FAIL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
|
||||||
|
WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
hr = E_FAIL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(period)
|
||||||
|
This->mmdev_period_rt = period;
|
||||||
|
else
|
||||||
|
This->mmdev_period_rt = DefaultPeriod;
|
||||||
|
|
||||||
|
/* Check if the ALSA buffer is so small that it will run out before
|
||||||
|
* the next MMDevAPI period tick occurs. Allow a little wiggle room
|
||||||
|
* with 120% of the period time. */
|
||||||
|
mmdev_period_frames = fmt->nSamplesPerSec * (This->mmdev_period_rt / 10000000.);
|
||||||
|
if(This->alsa_bufsize_frames < 1.2 * mmdev_period_frames)
|
||||||
|
FIXME("ALSA buffer time is smaller than our period time. Expect underruns. (%lu < %u)\n",
|
||||||
|
This->alsa_bufsize_frames, mmdev_period_frames);
|
||||||
|
|
||||||
This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
|
This->bufsize_frames = ceil((duration / 10000000.) * fmt->nSamplesPerSec);
|
||||||
This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
|
This->local_buffer = HeapAlloc(GetProcessHeap(), 0,
|
||||||
This->bufsize_frames * fmt->nBlockAlign);
|
This->bufsize_frames * fmt->nBlockAlign);
|
||||||
|
@ -987,76 +1007,6 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface,
|
||||||
else
|
else
|
||||||
memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
|
memset(This->local_buffer, 0, This->bufsize_frames * fmt->nBlockAlign);
|
||||||
|
|
||||||
if((err = snd_pcm_sw_params_get_boundary(sw_params, &boundary)) < 0){
|
|
||||||
WARN("Unable to get boundary: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_sw_params_set_start_threshold(This->pcm_handle,
|
|
||||||
sw_params, boundary)) < 0){
|
|
||||||
WARN("Unable to set start threshold to %lx: %d (%s)\n", boundary, err,
|
|
||||||
snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_sw_params_set_stop_threshold(This->pcm_handle,
|
|
||||||
sw_params, boundary)) < 0){
|
|
||||||
WARN("Unable to set stop threshold to %lx: %d (%s)\n", boundary, err,
|
|
||||||
snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_sw_params_set_avail_min(This->pcm_handle,
|
|
||||||
sw_params, 0)) < 0){
|
|
||||||
WARN("Unable to set avail min to 0: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_sw_params_set_silence_size(This->pcm_handle,
|
|
||||||
sw_params, boundary)) < 0){
|
|
||||||
WARN("Unable to set silence size to %lx: %d (%s)\n", boundary, err,
|
|
||||||
snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_sw_params(This->pcm_handle, sw_params)) < 0){
|
|
||||||
WARN("Unable to set sw params: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
|
|
||||||
WARN("Unable to prepare device: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_hw_params_get_buffer_size(This->hw_params,
|
|
||||||
&This->bufsize_alsa)) < 0){
|
|
||||||
WARN("Unable to get buffer size: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_hw_params_get_period_size(This->hw_params,
|
|
||||||
&This->period_alsa, NULL)) < 0){
|
|
||||||
WARN("Unable to get period size: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_hw_params_get_period_time(This->hw_params,
|
|
||||||
&This->period_us, NULL)) < 0){
|
|
||||||
WARN("Unable to get period time: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
hr = E_FAIL;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
This->fmt = clone_format(fmt);
|
This->fmt = clone_format(fmt);
|
||||||
if(!This->fmt){
|
if(!This->fmt){
|
||||||
hr = E_OUTOFMEMORY;
|
hr = E_OUTOFMEMORY;
|
||||||
|
@ -1181,12 +1131,13 @@ static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient *iface,
|
||||||
snd_pcm_sframes_t avail_frames;
|
snd_pcm_sframes_t avail_frames;
|
||||||
|
|
||||||
avail_frames = snd_pcm_avail_update(This->pcm_handle);
|
avail_frames = snd_pcm_avail_update(This->pcm_handle);
|
||||||
|
if(This->alsa_bufsize_frames < avail_frames ||
|
||||||
if(This->bufsize_alsa < avail_frames){
|
snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN)
|
||||||
WARN("Xrun detected\n");
|
|
||||||
*out = This->held_frames;
|
*out = This->held_frames;
|
||||||
}else
|
else
|
||||||
*out = This->bufsize_alsa - avail_frames + This->held_frames;
|
*out = This->alsa_bufsize_frames - avail_frames + This->held_frames;
|
||||||
|
TRACE("PCM state: %u, avail: %ld, pad: %u\n",
|
||||||
|
snd_pcm_state(This->pcm_handle), avail_frames, *out);
|
||||||
}else if(This->dataflow == eCapture){
|
}else if(This->dataflow == eCapture){
|
||||||
*out = This->held_frames;
|
*out = This->held_frames;
|
||||||
}else{
|
}else{
|
||||||
|
@ -1558,7 +1509,7 @@ static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
|
||||||
WARN("writei failed, recovering: %ld (%s)\n", written,
|
WARN("writei failed, recovering: %ld (%s)\n", written,
|
||||||
snd_strerror(written));
|
snd_strerror(written));
|
||||||
|
|
||||||
ret = wine_snd_pcm_recover(handle, written, 0);
|
ret = snd_pcm_recover(handle, written, 0);
|
||||||
if(ret < 0){
|
if(ret < 0){
|
||||||
WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
|
WARN("Could not recover: %d (%s)\n", ret, snd_strerror(ret));
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -1573,10 +1524,31 @@ static snd_pcm_sframes_t alsa_write_best_effort(snd_pcm_t *handle, BYTE *buf,
|
||||||
static void alsa_write_data(ACImpl *This)
|
static void alsa_write_data(ACImpl *This)
|
||||||
{
|
{
|
||||||
snd_pcm_sframes_t written;
|
snd_pcm_sframes_t written;
|
||||||
snd_pcm_uframes_t to_write;
|
snd_pcm_uframes_t to_write, avail;
|
||||||
|
int err;
|
||||||
BYTE *buf =
|
BYTE *buf =
|
||||||
This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
|
This->local_buffer + (This->lcl_offs_frames * This->fmt->nBlockAlign);
|
||||||
|
|
||||||
|
/* this call seems to be required to get an accurate snd_pcm_state() */
|
||||||
|
avail = snd_pcm_avail_update(This->pcm_handle);
|
||||||
|
|
||||||
|
if(snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_XRUN ||
|
||||||
|
avail > This->alsa_bufsize_frames){
|
||||||
|
TRACE("XRun state, recovering\n");
|
||||||
|
|
||||||
|
if((err = snd_pcm_recover(This->pcm_handle, -EPIPE, 1)) < 0)
|
||||||
|
WARN("snd_pcm_recover failed: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
|
||||||
|
if((err = snd_pcm_reset(This->pcm_handle)) < 0)
|
||||||
|
WARN("snd_pcm_reset failed: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
|
||||||
|
if((err = snd_pcm_prepare(This->pcm_handle)) < 0)
|
||||||
|
WARN("snd_pcm_prepare failed: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(This->held_frames == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
|
if(This->lcl_offs_frames + This->held_frames > This->bufsize_frames)
|
||||||
to_write = This->bufsize_frames - This->lcl_offs_frames;
|
to_write = This->bufsize_frames - This->lcl_offs_frames;
|
||||||
else
|
else
|
||||||
|
@ -1626,7 +1598,7 @@ static void alsa_read_data(ACImpl *This)
|
||||||
|
|
||||||
WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
|
WARN("read failed, recovering: %ld (%s)\n", nread, snd_strerror(nread));
|
||||||
|
|
||||||
ret = wine_snd_pcm_recover(This->pcm_handle, nread, 0);
|
ret = snd_pcm_recover(This->pcm_handle, nread, 0);
|
||||||
if(ret < 0){
|
if(ret < 0){
|
||||||
WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
|
WARN("Recover failed: %d (%s)\n", ret, snd_strerror(ret));
|
||||||
return;
|
return;
|
||||||
|
@ -1666,7 +1638,7 @@ static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
|
||||||
EnterCriticalSection(&This->lock);
|
EnterCriticalSection(&This->lock);
|
||||||
|
|
||||||
if(This->started){
|
if(This->started){
|
||||||
if(This->dataflow == eRender && This->held_frames)
|
if(This->dataflow == eRender)
|
||||||
alsa_write_data(This);
|
alsa_write_data(This);
|
||||||
else if(This->dataflow == eCapture)
|
else if(This->dataflow == eCapture)
|
||||||
alsa_read_data(This);
|
alsa_read_data(This);
|
||||||
|
@ -1678,36 +1650,9 @@ static void CALLBACK alsa_push_buffer_data(void *user, BOOLEAN timer)
|
||||||
LeaveCriticalSection(&This->lock);
|
LeaveCriticalSection(&This->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static HRESULT alsa_consider_start(ACImpl *This)
|
|
||||||
{
|
|
||||||
snd_pcm_sframes_t avail;
|
|
||||||
int err;
|
|
||||||
|
|
||||||
avail = snd_pcm_avail_update(This->pcm_handle);
|
|
||||||
if(avail < 0){
|
|
||||||
WARN("Unable to get avail_update: %ld (%s)\n", avail,
|
|
||||||
snd_strerror(avail));
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(This->period_alsa < This->bufsize_alsa - avail){
|
|
||||||
if((err = snd_pcm_start(This->pcm_handle)) < 0){
|
|
||||||
WARN("Start failed: %d (%s), state: %d\n", err, snd_strerror(err),
|
|
||||||
snd_pcm_state(This->pcm_handle));
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return S_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
|
static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
|
||||||
{
|
{
|
||||||
ACImpl *This = impl_from_IAudioClient(iface);
|
ACImpl *This = impl_from_IAudioClient(iface);
|
||||||
DWORD period_ms;
|
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
TRACE("(%p)\n", This);
|
TRACE("(%p)\n", This);
|
||||||
|
|
||||||
|
@ -1728,16 +1673,6 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
|
||||||
return AUDCLNT_E_NOT_STOPPED;
|
return AUDCLNT_E_NOT_STOPPED;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = alsa_consider_start(This);
|
|
||||||
if(FAILED(hr)){
|
|
||||||
LeaveCriticalSection(&This->lock);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
|
|
||||||
period_ms = This->period_us / 1000;
|
|
||||||
if(!period_ms)
|
|
||||||
period_ms = 10;
|
|
||||||
|
|
||||||
if(This->dataflow == eCapture){
|
if(This->dataflow == eCapture){
|
||||||
/* dump any data that might be leftover in the ALSA capture buffer */
|
/* dump any data that might be leftover in the ALSA capture buffer */
|
||||||
snd_pcm_readi(This->pcm_handle, This->local_buffer,
|
snd_pcm_readi(This->pcm_handle, This->local_buffer,
|
||||||
|
@ -1745,7 +1680,7 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
|
if(!CreateTimerQueueTimer(&This->timer, g_timer_q, alsa_push_buffer_data,
|
||||||
This, 0, period_ms, WT_EXECUTEINTIMERTHREAD)){
|
This, 0, This->mmdev_period_rt / 10000, WT_EXECUTEINTIMERTHREAD)){
|
||||||
LeaveCriticalSection(&This->lock);
|
LeaveCriticalSection(&This->lock);
|
||||||
WARN("Unable to create timer: %u\n", GetLastError());
|
WARN("Unable to create timer: %u\n", GetLastError());
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
|
@ -1761,7 +1696,6 @@ static HRESULT WINAPI AudioClient_Start(IAudioClient *iface)
|
||||||
static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
|
static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
|
||||||
{
|
{
|
||||||
ACImpl *This = impl_from_IAudioClient(iface);
|
ACImpl *This = impl_from_IAudioClient(iface);
|
||||||
int err;
|
|
||||||
HANDLE event;
|
HANDLE event;
|
||||||
BOOL wait;
|
BOOL wait;
|
||||||
|
|
||||||
|
@ -1779,24 +1713,21 @@ static HRESULT WINAPI AudioClient_Stop(IAudioClient *iface)
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(snd_pcm_drop(This->pcm_handle) < 0)
|
||||||
|
WARN("snd_pcm_drop failed\n");
|
||||||
|
|
||||||
|
if(snd_pcm_reset(This->pcm_handle) < 0)
|
||||||
|
WARN("snd_pcm_reset failed\n");
|
||||||
|
|
||||||
|
if(snd_pcm_prepare(This->pcm_handle) < 0)
|
||||||
|
WARN("snd_pcm_prepare failed\n");
|
||||||
|
|
||||||
event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
event = CreateEventW(NULL, TRUE, FALSE, NULL);
|
||||||
wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
|
wait = !DeleteTimerQueueTimer(g_timer_q, This->timer, event);
|
||||||
if(wait)
|
if(wait)
|
||||||
WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
|
WARN("DeleteTimerQueueTimer error %u\n", GetLastError());
|
||||||
wait = wait && GetLastError() == ERROR_IO_PENDING;
|
wait = wait && GetLastError() == ERROR_IO_PENDING;
|
||||||
|
|
||||||
if((err = snd_pcm_drop(This->pcm_handle)) < 0){
|
|
||||||
LeaveCriticalSection(&This->lock);
|
|
||||||
WARN("Drop failed: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((err = snd_pcm_prepare(This->pcm_handle)) < 0){
|
|
||||||
LeaveCriticalSection(&This->lock);
|
|
||||||
WARN("Prepare failed: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
This->started = FALSE;
|
This->started = FALSE;
|
||||||
|
|
||||||
LeaveCriticalSection(&This->lock);
|
LeaveCriticalSection(&This->lock);
|
||||||
|
@ -1831,6 +1762,15 @@ static HRESULT WINAPI AudioClient_Reset(IAudioClient *iface)
|
||||||
return AUDCLNT_E_BUFFER_OPERATION_PENDING;
|
return AUDCLNT_E_BUFFER_OPERATION_PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(snd_pcm_drop(This->pcm_handle) < 0)
|
||||||
|
WARN("snd_pcm_drop failed\n");
|
||||||
|
|
||||||
|
if(snd_pcm_reset(This->pcm_handle) < 0)
|
||||||
|
WARN("snd_pcm_reset failed\n");
|
||||||
|
|
||||||
|
if(snd_pcm_prepare(This->pcm_handle) < 0)
|
||||||
|
WARN("snd_pcm_prepare failed\n");
|
||||||
|
|
||||||
This->held_frames = 0;
|
This->held_frames = 0;
|
||||||
This->written_frames = 0;
|
This->written_frames = 0;
|
||||||
This->lcl_offs_frames = 0;
|
This->lcl_offs_frames = 0;
|
||||||
|
@ -2093,7 +2033,6 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
|
||||||
{
|
{
|
||||||
ACImpl *This = impl_from_IAudioRenderClient(iface);
|
ACImpl *This = impl_from_IAudioRenderClient(iface);
|
||||||
BYTE *buffer;
|
BYTE *buffer;
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
|
TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
|
||||||
|
|
||||||
|
@ -2118,43 +2057,10 @@ static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
|
||||||
memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
|
memset(buffer, 0, written_frames * This->fmt->nBlockAlign);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(This->held_frames){
|
if(This->buf_state == LOCKED_WRAPPED)
|
||||||
if(This->buf_state == LOCKED_WRAPPED)
|
alsa_wrap_buffer(This, buffer, written_frames);
|
||||||
alsa_wrap_buffer(This, buffer, written_frames);
|
|
||||||
|
|
||||||
This->held_frames += written_frames;
|
|
||||||
}else{
|
|
||||||
snd_pcm_sframes_t written;
|
|
||||||
|
|
||||||
written = alsa_write_best_effort(This->pcm_handle, buffer,
|
|
||||||
written_frames, This);
|
|
||||||
if(written < 0){
|
|
||||||
This->buf_state = NOT_LOCKED;
|
|
||||||
LeaveCriticalSection(&This->lock);
|
|
||||||
ERR("write failed: %ld (%s)\n", written, snd_strerror(written));
|
|
||||||
return E_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(written < written_frames){
|
|
||||||
if(This->buf_state == LOCKED_WRAPPED)
|
|
||||||
alsa_wrap_buffer(This,
|
|
||||||
This->tmp_buffer + written * This->fmt->nBlockAlign,
|
|
||||||
written_frames - written);
|
|
||||||
else
|
|
||||||
This->lcl_offs_frames += written;
|
|
||||||
This->held_frames = written_frames - written;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(This->started &&
|
|
||||||
snd_pcm_state(This->pcm_handle) == SND_PCM_STATE_PREPARED){
|
|
||||||
hr = alsa_consider_start(This);
|
|
||||||
if(FAILED(hr)){
|
|
||||||
LeaveCriticalSection(&This->lock);
|
|
||||||
return hr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
This->held_frames += written_frames;
|
||||||
This->written_frames += written_frames;
|
This->written_frames += written_frames;
|
||||||
This->buf_state = NOT_LOCKED;
|
This->buf_state = NOT_LOCKED;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue