call snd_pcm_hwsync() expclicitly before we access any of the status fields, since this seems to be necessary. try to find the right mixer device via the card index

git-svn-id: file:///home/lennart/svn/public/pulseaudio/branches/glitch-free@2221 fefdeb5f-60dc-0310-8127-8f9354f1896f
This commit is contained in:
Lennart Poettering 2008-04-07 17:19:51 +00:00
parent c84a64cf32
commit b3b8a63c50
2 changed files with 152 additions and 87 deletions

View file

@ -74,7 +74,7 @@ PA_MODULE_USAGE(
#define DEFAULT_DEVICE "default" #define DEFAULT_DEVICE "default"
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) #define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC)
#define DEFAULT_TSCHED_WATERMARK_USEC (100*PA_USEC_PER_MSEC) #define DEFAULT_TSCHED_WATERMARK_USEC (10*PA_USEC_PER_MSEC)
struct userdata { struct userdata {
pa_core *core; pa_core *core;
@ -145,6 +145,8 @@ static int mmap_write(struct userdata *u) {
const snd_pcm_channel_area_t *areas; const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t offset, frames; snd_pcm_uframes_t offset, frames;
snd_pcm_hwsync(u->pcm_handle);
/* First we determine how many samples are missing to fill the /* First we determine how many samples are missing to fill the
* buffer up to 100% */ * buffer up to 100% */
@ -152,20 +154,20 @@ static int mmap_write(struct userdata *u) {
pa_log_debug("snd_pcm_avail_update: %s", snd_strerror(n)); pa_log_debug("snd_pcm_avail_update: %s", snd_strerror(n));
if (n == -EPIPE) {
pa_log_debug("snd_pcm_avail_update: Buffer underrun!");
u->first = TRUE;
}
if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0)
continue;
if (err == -EAGAIN) { if (err == -EAGAIN) {
pa_log_debug("EAGAIN"); pa_log_debug("EAGAIN");
return work_done; return work_done;
} }
pa_log("snd_pcm_avail_update: %s", snd_strerror(err)); if (n == -EPIPE)
pa_log_debug("snd_pcm_avail_update: Buffer underrun!");
if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) {
u->first = TRUE;
continue;
}
pa_log("snd_pcm_recover: %s", snd_strerror(err));
return -1; return -1;
} }
@ -181,19 +183,19 @@ static int mmap_write(struct userdata *u) {
pa_log_debug("snd_pcm_mmap_begin: %s", snd_strerror(err)); pa_log_debug("snd_pcm_mmap_begin: %s", snd_strerror(err));
if (err == -EPIPE) {
pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!");
u->first = TRUE;
}
if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
continue;
if (err == -EAGAIN) { if (err == -EAGAIN) {
pa_log_debug("EAGAIN"); pa_log_debug("EAGAIN");
return work_done; return work_done;
} }
if (err == -EPIPE)
pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!");
if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
u->first = TRUE;
continue;
}
pa_log("Failed to write data to DSP: %s", snd_strerror(err)); pa_log("Failed to write data to DSP: %s", snd_strerror(err));
return -1; return -1;
} }
@ -222,19 +224,19 @@ static int mmap_write(struct userdata *u) {
pa_log_debug("snd_pcm_mmap_commit: %s", snd_strerror(err)); pa_log_debug("snd_pcm_mmap_commit: %s", snd_strerror(err));
if (err == -EPIPE) {
pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!");
u->first = TRUE;
}
if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0)
continue;
if (err == -EAGAIN) { if (err == -EAGAIN) {
pa_log_debug("EAGAIN"); pa_log_debug("EAGAIN");
return work_done; return work_done;
} }
if (err == -EPIPE)
pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!");
if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
u->first = TRUE;
continue;
}
pa_log("Failed to write data to DSP: %s", snd_strerror(err)); pa_log("Failed to write data to DSP: %s", snd_strerror(err));
return -1; return -1;
} }
@ -264,6 +266,8 @@ static int unix_write(struct userdata *u) {
snd_pcm_sframes_t n, frames; snd_pcm_sframes_t n, frames;
int err; int err;
snd_pcm_hwsync(u->pcm_handle);
if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) {
pa_log("Failed to query DSP status data: %s", snd_strerror(err)); pa_log("Failed to query DSP status data: %s", snd_strerror(err));
return -1; return -1;
@ -300,17 +304,19 @@ static int unix_write(struct userdata *u) {
if (PA_UNLIKELY(frames < 0)) { if (PA_UNLIKELY(frames < 0)) {
if (frames == -EPIPE)
pa_log_debug("snd_pcm_avail_update: Buffer underrun!");
if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0)
continue;
if (frames == -EAGAIN) { if (frames == -EAGAIN) {
pa_log_debug("EAGAIN"); pa_log_debug("EAGAIN");
return work_done; return work_done;
} }
if (frames == -EPIPE)
pa_log_debug("snd_pcm_avail_update: Buffer underrun!");
if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) {
u->first = TRUE;
continue;
}
pa_log("Failed to write data to DSP: %s", snd_strerror(frames)); pa_log("Failed to write data to DSP: %s", snd_strerror(frames));
return -1; return -1;
} }
@ -332,7 +338,7 @@ static int unix_write(struct userdata *u) {
} }
} }
static int update_smoother(struct userdata *u) { static void update_smoother(struct userdata *u) {
snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t delay = 0;
int64_t frames; int64_t frames;
int err; int err;
@ -343,11 +349,12 @@ static int update_smoother(struct userdata *u) {
/* Let's update the time smoother */ /* Let's update the time smoother */
snd_pcm_hwsync(u->pcm_handle);
snd_pcm_avail_update(u->pcm_handle); snd_pcm_avail_update(u->pcm_handle);
if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
pa_log("Failed to get delay: %s", snd_strerror(err)); pa_log_warn("Failed to get delay: %s", snd_strerror(err));
return -1; return;
} }
frames = u->frame_index - delay; frames = u->frame_index - delay;
@ -357,8 +364,6 @@ static int update_smoother(struct userdata *u) {
now1 = pa_rtclock_usec(); now1 = pa_rtclock_usec();
now2 = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec); now2 = pa_bytes_to_usec(frames * u->frame_size, &u->sink->sample_spec);
pa_smoother_put(u->smoother, now1, now2); pa_smoother_put(u->smoother, now1, now2);
return 0;
} }
static pa_usec_t sink_get_latency(struct userdata *u) { static pa_usec_t sink_get_latency(struct userdata *u) {
@ -814,6 +819,7 @@ static void thread_func(void *userdata) {
frames = u->sink->thread_info.rewind_nbytes / u->frame_size; frames = u->sink->thread_info.rewind_nbytes / u->frame_size;
snd_pcm_hwsync(u->pcm_handle);
if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) { if ((unused = snd_pcm_avail_update(u->pcm_handle)) < 0) {
pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused)); pa_log("snd_pcm_avail_update() failed: %s", snd_strerror(unused));
goto fail; goto fail;
@ -867,12 +873,11 @@ static void thread_func(void *userdata) {
pa_smoother_resume(u->smoother, pa_rtclock_usec()); pa_smoother_resume(u->smoother, pa_rtclock_usec());
} }
if (update_smoother(u) < 0) update_smoother(u);
goto fail;
} }
if (u->use_tsched) { if (u->use_tsched) {
pa_usec_t usec, cusec, sleep_usec; pa_usec_t usec, cusec;
/* OK, the playback buffer is now full, let's /* OK, the playback buffer is now full, let's
* calculate when to wake up next */ * calculate when to wake up next */
@ -887,19 +892,14 @@ static void thread_func(void *userdata) {
pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC); pa_log_debug("Waking up in %0.2fms (system clock).", (double) cusec / PA_USEC_PER_MSEC);
sleep_usec = PA_MIN(usec, cusec);
pa_log_debug("Waking up in %0.2fms (smaller value).", (double) sleep_usec / PA_USEC_PER_MSEC);
/* We don't trust the conversion, so we wake up whatever comes first */ /* We don't trust the conversion, so we wake up whatever comes first */
pa_rtpoll_set_timer_relative(u->rtpoll, sleep_usec); pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec));
} }
} else if (u->use_tsched) { } else if (u->use_tsched)
/* OK, we're in an invalid state, let's disable our timers */ /* OK, we're in an invalid state, let's disable our timers */
pa_rtpoll_set_timer_disabled(u->rtpoll); pa_rtpoll_set_timer_disabled(u->rtpoll);
}
/* Hmm, nothing to do. Let's sleep */ /* Hmm, nothing to do. Let's sleep */
if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
@ -923,6 +923,7 @@ static void thread_func(void *userdata) {
} }
if (revents & (POLLERR|POLLNVAL|POLLHUP)) { if (revents & (POLLERR|POLLNVAL|POLLHUP)) {
snd_pcm_state_t state;
if (revents & POLLERR) if (revents & POLLERR)
pa_log_warn("Got POLLERR from ALSA"); pa_log_warn("Got POLLERR from ALSA");
@ -931,9 +932,12 @@ static void thread_func(void *userdata) {
if (revents & POLLHUP) if (revents & POLLHUP)
pa_log_warn("Got POLLHUP from ALSA"); pa_log_warn("Got POLLHUP from ALSA");
state = snd_pcm_state(u->pcm_handle);
pa_log_warn("PCM state is %s", snd_pcm_state_name(state));
/* Try to recover from this error */ /* Try to recover from this error */
switch (snd_pcm_state(u->pcm_handle)) { switch (state) {
case SND_PCM_STATE_XRUN: case SND_PCM_STATE_XRUN:
if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) { if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) {
@ -959,6 +963,8 @@ static void thread_func(void *userdata) {
} }
break; break;
} }
u->first = TRUE;
} }
pa_log_debug("alsa revents = %i", revents); pa_log_debug("alsa revents = %i", revents);
@ -1132,15 +1138,26 @@ int pa__init(pa_module*m) {
if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0) if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
found = TRUE; found = TRUE;
else if (dev_id) { else {
char *md = pa_sprintf_malloc("hw:%s", dev_id); snd_pcm_info_t *info;
snd_pcm_info_alloca(&info);
if (snd_pcm_info(u->pcm_handle, info) >= 0) {
char *md;
int card;
if ((card = snd_pcm_info_get_card(info)) >= 0) {
md = pa_sprintf_malloc("hw:%i", card);
if (strcmp(u->device_name, md)) if (strcmp(u->device_name, md))
if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0) if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
found = TRUE; found = TRUE;
pa_xfree(md); pa_xfree(md);
} }
}
}
if (found) if (found)
if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM"))) if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Master", "PCM")))
@ -1304,6 +1321,8 @@ int pa__init(pa_module*m) {
} else } else
u->mixer_fdl = NULL; u->mixer_fdl = NULL;
pa_alsa_dump(u->pcm_handle);
if (!(u->thread = pa_thread_new(thread_func, u))) { if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread."); pa_log("Failed to create thread.");
goto fail; goto fail;

View file

@ -141,18 +141,26 @@ static int mmap_read(struct userdata *u) {
pa_memchunk chunk; pa_memchunk chunk;
void *p; void *p;
snd_pcm_hwsync(u->pcm_handle);
if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) { if (PA_UNLIKELY((n = snd_pcm_avail_update(u->pcm_handle)) < 0)) {
pa_log_debug("snd_pcm_avail_update: %s", snd_strerror(n));
if (err == -EAGAIN) {
pa_log_debug("EAGAIN");
return work_done;
}
if (n == -EPIPE) if (n == -EPIPE)
pa_log_debug("snd_pcm_avail_update: Buffer underrun!"); pa_log_debug("snd_pcm_avail_update: Buffer underrun!");
if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) if ((err = snd_pcm_recover(u->pcm_handle, n, 1)) == 0) {
snd_pcm_start(u->pcm_handle);
continue; continue;
}
if (err == -EAGAIN) pa_log("snd_pcm_recover: %s", snd_strerror(err));
return work_done;
pa_log("snd_pcm_avail_update: %s", snd_strerror(err));
return -1; return -1;
} }
@ -163,14 +171,20 @@ static int mmap_read(struct userdata *u) {
if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) { if (PA_UNLIKELY((err = snd_pcm_mmap_begin(u->pcm_handle, &areas, &offset, &frames)) < 0)) {
pa_log_debug("snd_pcm_mmap_begin: %s", snd_strerror(err));
if (err == -EAGAIN) {
pa_log_debug("EAGAIN");
return work_done;
}
if (err == -EPIPE) if (err == -EPIPE)
pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!"); pa_log_debug("snd_pcm_mmap_begin: Buffer underrun!");
if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
snd_pcm_start(u->pcm_handle);
continue; continue;
}
if (err == -EAGAIN)
return work_done;
pa_log("Failed to write data to DSP: %s", snd_strerror(err)); pa_log("Failed to write data to DSP: %s", snd_strerror(err));
return -1; return -1;
@ -198,14 +212,20 @@ static int mmap_read(struct userdata *u) {
if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) { if (PA_UNLIKELY((err = snd_pcm_mmap_commit(u->pcm_handle, offset, frames)) < 0)) {
pa_log_debug("snd_pcm_mmap_commit: %s", snd_strerror(err));
if (err == -EAGAIN) {
pa_log_debug("EAGAIN");
return work_done;
}
if (err == -EPIPE) if (err == -EPIPE)
pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!"); pa_log_debug("snd_pcm_mmap_commit: Buffer underrun!");
if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) == 0) {
snd_pcm_start(u->pcm_handle);
continue; continue;
}
if (err == -EAGAIN)
return work_done;
pa_log("Failed to write data to DSP: %s", snd_strerror(err)); pa_log("Failed to write data to DSP: %s", snd_strerror(err));
return -1; return -1;
@ -215,6 +235,8 @@ static int mmap_read(struct userdata *u) {
u->frame_index += frames; u->frame_index += frames;
pa_log_debug("read %llu frames", (unsigned long long) frames);
if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n)) if (PA_LIKELY(frames >= (snd_pcm_uframes_t) n))
return work_done; return work_done;
} }
@ -235,6 +257,8 @@ static int unix_read(struct userdata *u) {
int err; int err;
pa_memchunk chunk; pa_memchunk chunk;
snd_pcm_hwsync(u->pcm_handle);
if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) { if (PA_UNLIKELY((err = snd_pcm_status(u->pcm_handle, status)) < 0)) {
pa_log("Failed to query DSP status data: %s", snd_strerror(err)); pa_log("Failed to query DSP status data: %s", snd_strerror(err));
return -1; return -1;
@ -264,11 +288,17 @@ static int unix_read(struct userdata *u) {
if (PA_UNLIKELY(frames < 0)) { if (PA_UNLIKELY(frames < 0)) {
pa_memblock_unref(chunk.memblock); pa_memblock_unref(chunk.memblock);
if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0) if (frames == -EAGAIN) {
continue; pa_log_debug("EAGAIN");
if (frames == -EAGAIN)
return work_done; return work_done;
}
if (frames == -EPIPE)
pa_log_debug("snd_pcm_avail_update: Buffer overrun!");
if ((frames = snd_pcm_recover(u->pcm_handle, frames, 1)) == 0)
snd_pcm_start(u->pcm_handle);
continue;
pa_log("Failed to read data from DSP: %s", snd_strerror(frames)); pa_log("Failed to read data from DSP: %s", snd_strerror(frames));
return -1; return -1;
@ -289,7 +319,7 @@ static int unix_read(struct userdata *u) {
} }
} }
static int update_smoother(struct userdata *u) { static void update_smoother(struct userdata *u) {
snd_pcm_sframes_t delay = 0; snd_pcm_sframes_t delay = 0;
int64_t frames; int64_t frames;
int err; int err;
@ -300,11 +330,12 @@ static int update_smoother(struct userdata *u) {
/* Let's update the time smoother */ /* Let's update the time smoother */
snd_pcm_hwsync(u->pcm_handle);
snd_pcm_avail_update(u->pcm_handle); snd_pcm_avail_update(u->pcm_handle);
if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) { if (PA_UNLIKELY((err = snd_pcm_delay(u->pcm_handle, &delay)) < 0)) {
pa_log("Failed to get delay: %s", snd_strerror(err)); pa_log_warn("Failed to get delay: %s", snd_strerror(err));
return -1; return;
} }
frames = u->frame_index + delay; frames = u->frame_index + delay;
@ -312,8 +343,6 @@ static int update_smoother(struct userdata *u) {
now1 = pa_rtclock_usec(); now1 = pa_rtclock_usec();
now2 = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec); now2 = pa_bytes_to_usec(frames * u->frame_size, &u->source->sample_spec);
pa_smoother_put(u->smoother, now1, now2); pa_smoother_put(u->smoother, now1, now2);
return 0;
} }
static pa_usec_t source_get_latency(struct userdata *u) { static pa_usec_t source_get_latency(struct userdata *u) {
@ -728,8 +757,7 @@ static void thread_func(void *userdata) {
} }
if (work_done) if (work_done)
if (update_smoother(u) < 0) update_smoother(u);
goto fail;
if (u->use_tsched) { if (u->use_tsched) {
pa_usec_t usec, cusec; pa_usec_t usec, cusec;
@ -750,11 +778,10 @@ static void thread_func(void *userdata) {
/* We don't trust the conversion, so we wake up whatever comes first */ /* We don't trust the conversion, so we wake up whatever comes first */
pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec)); pa_rtpoll_set_timer_relative(u->rtpoll, PA_MIN(usec, cusec));
} }
} else if (u->use_tsched) { } else if (u->use_tsched)
/* OK, we're in an invalid state, let's disable our timers */ /* OK, we're in an invalid state, let's disable our timers */
pa_rtpoll_set_timer_disabled(u->rtpoll); pa_rtpoll_set_timer_disabled(u->rtpoll);
}
/* Hmm, nothing to do. Let's sleep */ /* Hmm, nothing to do. Let's sleep */
if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0) if ((ret = pa_rtpoll_run(u->rtpoll, 1)) < 0)
@ -778,6 +805,7 @@ static void thread_func(void *userdata) {
} }
if (revents & (POLLERR|POLLNVAL|POLLHUP)) { if (revents & (POLLERR|POLLNVAL|POLLHUP)) {
snd_pcm_state_t state;
if (revents & POLLERR) if (revents & POLLERR)
pa_log_warn("Got POLLERR from ALSA"); pa_log_warn("Got POLLERR from ALSA");
@ -786,9 +814,12 @@ static void thread_func(void *userdata) {
if (revents & POLLHUP) if (revents & POLLHUP)
pa_log_warn("Got POLLHUP from ALSA"); pa_log_warn("Got POLLHUP from ALSA");
state = snd_pcm_state(u->pcm_handle);
pa_log_warn("PCM state is %s", snd_pcm_state_name(state));
/* Try to recover from this error */ /* Try to recover from this error */
switch (snd_pcm_state(u->pcm_handle)) { switch (state) {
case SND_PCM_STATE_XRUN: case SND_PCM_STATE_XRUN:
if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) { if ((err = snd_pcm_recover(u->pcm_handle, -EPIPE, 1)) != 0) {
@ -814,6 +845,8 @@ static void thread_func(void *userdata) {
} }
break; break;
} }
snd_pcm_start(u->pcm_handle);
} }
} }
} }
@ -979,15 +1012,26 @@ int pa__init(pa_module*m) {
if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0) if (pa_alsa_prepare_mixer(u->mixer_handle, u->device_name) >= 0)
found = TRUE; found = TRUE;
else if (dev_id) { else {
char *md = pa_sprintf_malloc("hw:%s", dev_id); snd_pcm_info_t* info;
snd_pcm_info_alloca(&info);
if (snd_pcm_info(u->pcm_handle, info) >= 0) {
char *md;
int card;
if ((card = snd_pcm_info_get_card(info)) >= 0) {
md = pa_sprintf_malloc("hw:%i", card);
if (strcmp(u->device_name, md)) if (strcmp(u->device_name, md))
if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0) if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
found = TRUE; found = TRUE;
pa_xfree(md); pa_xfree(md);
} }
}
}
if (found) if (found)
if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic"))) if (!(u->mixer_elem = pa_alsa_find_elem(u->mixer_handle, "Capture", "Mic")))
@ -1138,6 +1182,8 @@ int pa__init(pa_module*m) {
} else } else
u->mixer_fdl = NULL; u->mixer_fdl = NULL;
pa_alsa_dump(u->pcm_handle);
if (!(u->thread = pa_thread_new(thread_func, u))) { if (!(u->thread = pa_thread_new(thread_func, u))) {
pa_log("Failed to create thread."); pa_log("Failed to create thread.");
goto fail; goto fail;