alsa: rework buffer/period configuration

- As discussed on alsa-devel it's probably better to initialize the
  buffer size first, followed by the period size. If that fails try the
  other way round. If that fails try to configure only buffer size. If
  that fails try to configure only period size. Finally, try to
  configure neither.

- Don't require integral periods anymore.

Both of these changes should help improving compatibility with various
weirder sound devices, such as TV cards.
This commit is contained in:
Lennart Poettering 2009-09-09 04:28:52 +02:00
parent 71e066c873
commit 557c429510
4 changed files with 216 additions and 129 deletions

View file

@ -116,7 +116,6 @@ struct userdata {
pa_usec_t watermark_dec_not_before; pa_usec_t watermark_dec_not_before;
unsigned nfragments;
pa_memchunk memchunk; pa_memchunk memchunk;
char *device_name; /* name of the PCM device */ char *device_name; /* name of the PCM device */
@ -943,8 +942,7 @@ static int unsuspend(struct userdata *u) {
pa_sample_spec ss; pa_sample_spec ss;
int err; int err;
pa_bool_t b, d; pa_bool_t b, d;
unsigned nfrags; snd_pcm_uframes_t period_size, buffer_size;
snd_pcm_uframes_t period_size;
pa_assert(u); pa_assert(u);
pa_assert(!u->pcm_handle); pa_assert(!u->pcm_handle);
@ -961,12 +959,12 @@ static int unsuspend(struct userdata *u) {
} }
ss = u->sink->sample_spec; ss = u->sink->sample_spec;
nfrags = u->nfragments;
period_size = u->fragment_size / u->frame_size; period_size = u->fragment_size / u->frame_size;
buffer_size = u->hwbuf_size / u->frame_size;
b = u->use_mmap; b = u->use_mmap;
d = u->use_tsched; d = u->use_tsched;
if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) { if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, TRUE)) < 0) {
pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err)); pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err));
goto fail; goto fail;
} }
@ -981,10 +979,11 @@ static int unsuspend(struct userdata *u) {
goto fail; goto fail;
} }
if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { if (period_size*u->frame_size != u->fragment_size ||
pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)", buffer_size*u->frame_size != u->hwbuf_size) {
(unsigned long) u->nfragments, (unsigned long) u->fragment_size, pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)",
(unsigned long) nfrags, period_size * u->frame_size); (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size,
(unsigned long) (buffer_size*u->fragment_size), (unsigned long) (period_size*u->frame_size));
goto fail; goto fail;
} }
@ -1638,8 +1637,8 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
const char *dev_id = NULL; const char *dev_id = NULL;
pa_sample_spec ss, requested_ss; pa_sample_spec ss, requested_ss;
pa_channel_map map; pa_channel_map map;
uint32_t nfrags, frag_size, tsched_size, tsched_watermark; uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark;
snd_pcm_uframes_t period_frames, tsched_frames; snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames;
size_t frame_size; size_t frame_size;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_sink_new_data data; pa_sink_new_data data;
@ -1673,7 +1672,10 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
goto fail; goto fail;
} }
buffer_size = nfrags * frag_size;
period_frames = frag_size/frame_size; period_frames = frag_size/frame_size;
buffer_frames = buffer_size/frame_size;
tsched_frames = tsched_size/frame_size; tsched_frames = tsched_size/frame_size;
if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
@ -1740,7 +1742,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames, &period_frames, &buffer_frames, tsched_frames,
&b, &d, mapping))) &b, &d, mapping)))
goto fail; goto fail;
@ -1755,7 +1757,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames, &period_frames, &buffer_frames, tsched_frames,
&b, &d, profile_set, &mapping))) &b, &d, profile_set, &mapping)))
goto fail; goto fail;
@ -1767,7 +1769,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames, &period_frames, &buffer_frames, tsched_frames,
&b, &d, FALSE))) &b, &d, FALSE)))
goto fail; goto fail;
} }
@ -1819,7 +1821,7 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle); pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (buffer_frames * frame_size));
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
@ -1860,13 +1862,15 @@ pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_ca
pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_set_rtpoll(u->sink, u->rtpoll);
u->frame_size = frame_size; u->frame_size = frame_size;
u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); u->fragment_size = frag_size = (size_t) (period_frames * frame_size);
u->nfragments = nfrags; u->hwbuf_size = buffer_size = (size_t) (buffer_frames * frame_size);
u->hwbuf_size = u->fragment_size * nfrags;
pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels); pa_cvolume_mute(&u->hardware_volume, u->sink->sample_spec.channels);
pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", pa_log_info("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)",
nfrags, (long unsigned) u->fragment_size, (double) u->hwbuf_size / (double) u->fragment_size,
(long unsigned) u->fragment_size,
(double) pa_bytes_to_usec(u->fragment_size, &ss) / PA_USEC_PER_MSEC,
(long unsigned) u->hwbuf_size,
(double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
pa_sink_set_max_request(u->sink, u->hwbuf_size); pa_sink_set_max_request(u->sink, u->hwbuf_size);

View file

@ -111,8 +111,6 @@ struct userdata {
pa_usec_t watermark_dec_not_before; pa_usec_t watermark_dec_not_before;
unsigned nfragments;
char *device_name; char *device_name;
char *control_device; char *control_device;
@ -891,8 +889,7 @@ static int unsuspend(struct userdata *u) {
pa_sample_spec ss; pa_sample_spec ss;
int err; int err;
pa_bool_t b, d; pa_bool_t b, d;
unsigned nfrags; snd_pcm_uframes_t period_size, buffer_size;
snd_pcm_uframes_t period_size;
pa_assert(u); pa_assert(u);
pa_assert(!u->pcm_handle); pa_assert(!u->pcm_handle);
@ -909,12 +906,12 @@ static int unsuspend(struct userdata *u) {
} }
ss = u->source->sample_spec; ss = u->source->sample_spec;
nfrags = u->nfragments;
period_size = u->fragment_size / u->frame_size; period_size = u->fragment_size / u->frame_size;
buffer_size = u->hwbuf_size / u->frame_size;
b = u->use_mmap; b = u->use_mmap;
d = u->use_tsched; d = u->use_tsched;
if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) { if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, TRUE)) < 0) {
pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err)); pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err));
goto fail; goto fail;
} }
@ -929,10 +926,11 @@ static int unsuspend(struct userdata *u) {
goto fail; goto fail;
} }
if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { if (period_size*u->frame_size != u->fragment_size ||
pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)", buffer_size*u->frame_size != u->hwbuf_size) {
(unsigned long) u->nfragments, (unsigned long) u->fragment_size, pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)",
(unsigned long) nfrags, period_size * u->frame_size); (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size,
(unsigned long) (buffer_size*u->fragment_size), (unsigned long) (period_size*u->frame_size));
goto fail; goto fail;
} }
@ -1483,8 +1481,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
const char *dev_id = NULL; const char *dev_id = NULL;
pa_sample_spec ss, requested_ss; pa_sample_spec ss, requested_ss;
pa_channel_map map; pa_channel_map map;
uint32_t nfrags, frag_size, tsched_size, tsched_watermark; uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark;
snd_pcm_uframes_t period_frames, tsched_frames; snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames;
size_t frame_size; size_t frame_size;
pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_source_new_data data; pa_source_new_data data;
@ -1518,7 +1516,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
goto fail; goto fail;
} }
buffer_size = nfrags * frag_size;
period_frames = frag_size/frame_size; period_frames = frag_size/frame_size;
buffer_frames = buffer_size/frame_size;
tsched_frames = tsched_size/frame_size; tsched_frames = tsched_size/frame_size;
if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) {
@ -1584,7 +1585,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_CAPTURE, SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames, &period_frames, &buffer_frames, tsched_frames,
&b, &d, mapping))) &b, &d, mapping)))
goto fail; goto fail;
@ -1598,7 +1599,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_CAPTURE, SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames, &period_frames, &buffer_frames, tsched_frames,
&b, &d, profile_set, &mapping))) &b, &d, profile_set, &mapping)))
goto fail; goto fail;
@ -1609,7 +1610,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
&u->device_name, &u->device_name,
&ss, &map, &ss, &map,
SND_PCM_STREAM_CAPTURE, SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames, &period_frames, &buffer_frames, tsched_frames,
&b, &d, FALSE))) &b, &d, FALSE)))
goto fail; goto fail;
} }
@ -1661,7 +1662,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle); pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name);
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (buffer_frames * frame_size));
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
@ -1702,13 +1703,15 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p
pa_source_set_rtpoll(u->source, u->rtpoll); pa_source_set_rtpoll(u->source, u->rtpoll);
u->frame_size = frame_size; u->frame_size = frame_size;
u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); u->fragment_size = frag_size = (size_t) (period_frames * frame_size);
u->nfragments = nfrags; u->hwbuf_size = buffer_size = (size_t) (buffer_frames * frame_size);
u->hwbuf_size = u->fragment_size * nfrags;
pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels); pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels);
pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", pa_log_info("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)",
nfrags, (long unsigned) u->fragment_size, (double) u->hwbuf_size / (double) u->fragment_size,
(long unsigned) u->fragment_size,
(double) pa_bytes_to_usec(u->fragment_size, &ss) / PA_USEC_PER_MSEC,
(long unsigned) u->hwbuf_size,
(double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC);
if (u->use_tsched) { if (u->use_tsched) {

View file

@ -93,6 +93,7 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
int ret; int ret;
pa_assert(pcm_handle); pa_assert(pcm_handle);
pa_assert(hwparams);
pa_assert(f); pa_assert(f);
if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0) if ((ret = snd_pcm_hw_params_set_format(pcm_handle, hwparams, format_trans[*f])) >= 0)
@ -148,33 +149,71 @@ try_auto:
return -1; return -1;
} }
static int set_period_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_uframes_t size) {
snd_pcm_uframes_t s;
int d, ret;
pa_assert(pcm_handle);
pa_assert(hwparams);
s = size;
d = 0;
if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d) < 0) {
s = size;
d = -1;
if (snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d) < 0) {
s = size;
d = 1;
if ((ret = snd_pcm_hw_params_set_period_size_near(pcm_handle, hwparams, &s, &d)) < 0) {
pa_log_info("snd_pcm_hw_params_set_period_size_near() failed: %s", pa_alsa_strerror(ret));
return ret;
}
}
}
return 0;
}
static int set_buffer_size(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, snd_pcm_uframes_t size) {
int ret;
pa_assert(pcm_handle);
pa_assert(hwparams);
if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &size)) < 0) {
pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret));
return ret;
}
return 0;
}
/* Set the hardware parameters of the given ALSA device. Returns the /* Set the hardware parameters of the given ALSA device. Returns the
* selected fragment settings in *period and *period_size */ * selected fragment settings in *period and *period_size */
int pa_alsa_set_hw_params( int pa_alsa_set_hw_params(
snd_pcm_t *pcm_handle, snd_pcm_t *pcm_handle,
pa_sample_spec *ss, pa_sample_spec *ss,
uint32_t *periods,
snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, pa_bool_t *use_mmap,
pa_bool_t *use_tsched, pa_bool_t *use_tsched,
pa_bool_t require_exact_channel_number) { pa_bool_t require_exact_channel_number) {
int ret = -1; int ret = -1;
snd_pcm_hw_params_t *hwparams, *hwparams_copy;
int dir;
snd_pcm_uframes_t _period_size = period_size ? *period_size : 0; snd_pcm_uframes_t _period_size = period_size ? *period_size : 0;
unsigned int _periods = periods ? *periods : 0; snd_pcm_uframes_t _buffer_size = buffer_size ? *buffer_size : 0;
unsigned int r = ss->rate;
unsigned int c = ss->channels;
pa_sample_format_t f = ss->format;
snd_pcm_hw_params_t *hwparams;
pa_bool_t _use_mmap = use_mmap && *use_mmap; pa_bool_t _use_mmap = use_mmap && *use_mmap;
pa_bool_t _use_tsched = use_tsched && *use_tsched; pa_bool_t _use_tsched = use_tsched && *use_tsched;
int dir; pa_sample_spec _ss = *ss;
pa_assert(pcm_handle); pa_assert(pcm_handle);
pa_assert(ss); pa_assert(ss);
snd_pcm_hw_params_alloca(&hwparams); snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_hw_params_alloca(&hwparams_copy);
if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) { if ((ret = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) {
pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret)); pa_log_debug("snd_pcm_hw_params_any() failed: %s", pa_alsa_strerror(ret));
@ -208,114 +247,140 @@ int pa_alsa_set_hw_params(
if (!_use_mmap) if (!_use_mmap)
_use_tsched = FALSE; _use_tsched = FALSE;
if ((ret = set_format(pcm_handle, hwparams, &f)) < 0) if ((ret = set_format(pcm_handle, hwparams, &_ss.format)) < 0)
goto finish; goto finish;
if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0) { if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &_ss.rate, NULL)) < 0) {
pa_log_debug("snd_pcm_hw_params_set_rate_near() failed: %s", pa_alsa_strerror(ret)); pa_log_debug("snd_pcm_hw_params_set_rate_near() failed: %s", pa_alsa_strerror(ret));
goto finish; goto finish;
} }
if (require_exact_channel_number) { if (require_exact_channel_number) {
if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) { if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, _ss.channels)) < 0) {
pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", c, pa_alsa_strerror(ret)); pa_log_debug("snd_pcm_hw_params_set_channels(%u) failed: %s", _ss.channels, pa_alsa_strerror(ret));
goto finish; goto finish;
} }
} else { } else {
unsigned int c = _ss.channels;
if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0) { if ((ret = snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c)) < 0) {
pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", c, pa_alsa_strerror(ret)); pa_log_debug("snd_pcm_hw_params_set_channels_near(%u) failed: %s", _ss.channels, pa_alsa_strerror(ret));
goto finish; goto finish;
} }
_ss.channels = c;
} }
if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) { if (_use_tsched && tsched_size > 0) {
pa_log_debug("snd_pcm_hw_params_set_periods_integer() failed: %s", pa_alsa_strerror(ret)); _buffer_size = pa_convert_size(tsched_size, ss, &_ss);
goto finish; _period_size = _buffer_size;
} else {
_period_size = pa_convert_size(_period_size, ss, &_ss);
_buffer_size = pa_convert_size(_buffer_size, ss, &_ss);
} }
if (_period_size > 0 && tsched_size > 0 && _periods > 0) { if (_buffer_size > 0 || _period_size > 0) {
snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t max_frames = 0;
unsigned int p;
/* Adjust the buffer sizes, if we didn't get the rate we were asking for */ if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &max_frames)) < 0)
_period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate); pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret));
tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate); else
pa_log_debug("Maximum hw buffer size is %lu ms", (long unsigned) max_frames * PA_MSEC_PER_SEC / _ss.rate);
if (_use_tsched) {
buffer_size = 0;
if ((ret = snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size)) < 0)
pa_log_warn("snd_pcm_hw_params_get_buffer_size_max() failed: %s", pa_alsa_strerror(ret));
else
pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r);
_period_size = tsched_size;
_periods = 1;
}
/* Some ALSA drivers really don't like if we set the buffer /* Some ALSA drivers really don't like if we set the buffer
* size first and the number of periods second. (which would * size first and the number of periods second. (which would
* make a lot more sense to me) So, follow this rule and * make a lot more sense to me) So, try a few combinations
* adjust the periods first and the buffer size second */ * before we give up. */
/* First we pass 0 as direction to get exactly what we if (_buffer_size > 0 && _period_size > 0) {
* asked for. That this is necessary is presumably a bug snd_pcm_hw_params_copy(hwparams_copy, hwparams);
* in ALSA. All in all this is mostly a hint to ALSA, so
* we don't care if this fails. */
p = _periods; /* First try: set buffer size first, followed by period size */
dir = 0; if (set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 &&
if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 &&
p = _periods; snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) {
dir = 1; pa_log_debug("Set buffer size first, period size second.");
if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir) < 0) { goto success;
p = _periods; }
dir = -1;
if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &p, &dir)) < 0) /* Second try: set period size first, followed by buffer size */
pa_log_info("snd_pcm_hw_params_set_periods_near() failed: %s", pa_alsa_strerror(ret)); if (set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 &&
set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 &&
snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) {
pa_log_debug("Set period size first, buffer size second.");
goto success;
} }
} }
/* Now set the buffer size */ if (_buffer_size > 0) {
buffer_size = _periods * _period_size; snd_pcm_hw_params_copy(hwparams_copy, hwparams);
if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0)
pa_log_info("snd_pcm_hw_params_set_buffer_size_near() failed: %s", pa_alsa_strerror(ret)); /* Third try: set only buffer size */
if (set_buffer_size(pcm_handle, hwparams_copy, _buffer_size) >= 0 &&
snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) {
pa_log_debug("Set only buffer size second.");
goto success;
}
}
if (_period_size > 0) {
snd_pcm_hw_params_copy(hwparams_copy, hwparams);
/* Fourth try: set only period size */
if (set_period_size(pcm_handle, hwparams_copy, _period_size) >= 0 &&
snd_pcm_hw_params(pcm_handle, hwparams_copy) >= 0) {
pa_log_debug("Set only period size second.");
goto success;
}
}
} }
if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) pa_log_debug("Set neither period nor buffer size.");
/* Last chance, set nothing */
if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) {
pa_log_info("snd_pcm_hw_params failed: %s", pa_alsa_strerror(ret));
goto finish; goto finish;
}
if (ss->rate != r) success:
pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r);
if (ss->channels != c) if (ss->rate != _ss.rate)
pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c); pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, _ss.rate);
if (ss->format != f) if (ss->channels != _ss.channels)
pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f)); pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, _ss.channels);
if (ss->format != _ss.format)
pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(_ss.format));
if ((ret = snd_pcm_prepare(pcm_handle)) < 0) { if ((ret = snd_pcm_prepare(pcm_handle)) < 0) {
pa_log_info("snd_pcm_prepare() failed: %s", pa_alsa_strerror(ret)); pa_log_info("snd_pcm_prepare() failed: %s", pa_alsa_strerror(ret));
goto finish; goto finish;
} }
if ((ret = snd_pcm_hw_params_current(pcm_handle, hwparams)) < 0) {
pa_log_info("snd_pcm_hw_params_current() failed: %s", pa_alsa_strerror(ret));
goto finish;
}
if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 || if ((ret = snd_pcm_hw_params_get_period_size(hwparams, &_period_size, &dir)) < 0 ||
(ret = snd_pcm_hw_params_get_periods(hwparams, &_periods, &dir)) < 0) { (ret = snd_pcm_hw_params_get_buffer_size(hwparams, &_buffer_size)) < 0) {
pa_log_info("snd_pcm_hw_params_get_period{s|_size}() failed: %s", pa_alsa_strerror(ret)); pa_log_info("snd_pcm_hw_params_get_{period|buffer}_size() failed: %s", pa_alsa_strerror(ret));
goto finish; goto finish;
} }
/* If the sample rate deviates too much, we need to resample */ /* If the sample rate deviates too much, we need to resample */
if (r < ss->rate*.95 || r > ss->rate*1.05) if (_ss.rate < ss->rate*.95 || _ss.rate > ss->rate*1.05)
ss->rate = r; ss->rate = _ss.rate;
ss->channels = (uint8_t) c; ss->channels = _ss.channels;
ss->format = f; ss->format = _ss.format;
pa_assert(_periods > 0);
pa_assert(_period_size > 0); pa_assert(_period_size > 0);
pa_assert(_buffer_size > 0);
if (periods) if (buffer_size)
*periods = _periods; *buffer_size = _buffer_size;
if (period_size) if (period_size)
*period_size = _period_size; *period_size = _period_size;
@ -393,8 +458,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
pa_sample_spec *ss, pa_sample_spec *ss,
pa_channel_map* map, pa_channel_map* map,
int mode, int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, pa_bool_t *use_mmap,
pa_bool_t *use_tsched, pa_bool_t *use_tsched,
@ -410,8 +475,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
pa_assert(dev); pa_assert(dev);
pa_assert(ss); pa_assert(ss);
pa_assert(map); pa_assert(map);
pa_assert(nfrags);
pa_assert(period_size);
pa_assert(ps); pa_assert(ps);
/* First we try to find a device string with a superset of the /* First we try to find a device string with a superset of the
@ -433,8 +496,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
ss, ss,
map, map,
mode, mode,
nfrags,
period_size, period_size,
buffer_size,
tsched_size, tsched_size,
use_mmap, use_mmap,
use_tsched, use_tsched,
@ -460,8 +523,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
ss, ss,
map, map,
mode, mode,
nfrags,
period_size, period_size,
buffer_size,
tsched_size, tsched_size,
use_mmap, use_mmap,
use_tsched, use_tsched,
@ -478,7 +541,18 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
/* OK, we didn't find any good device, so let's try the raw hw: stuff */ /* OK, we didn't find any good device, so let's try the raw hw: stuff */
d = pa_sprintf_malloc("hw:%s", dev_id); d = pa_sprintf_malloc("hw:%s", dev_id);
pa_log_debug("Trying %s as last resort...", d); pa_log_debug("Trying %s as last resort...", d);
pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE); pcm_handle = pa_alsa_open_by_device_string(
d,
dev,
ss,
map,
mode,
period_size,
buffer_size,
tsched_size,
use_mmap,
use_tsched,
FALSE);
pa_xfree(d); pa_xfree(d);
if (pcm_handle && mapping) if (pcm_handle && mapping)
@ -493,8 +567,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
pa_sample_spec *ss, pa_sample_spec *ss,
pa_channel_map* map, pa_channel_map* map,
int mode, int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, pa_bool_t *use_mmap,
pa_bool_t *use_tsched, pa_bool_t *use_tsched,
@ -508,8 +582,6 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
pa_assert(dev); pa_assert(dev);
pa_assert(ss); pa_assert(ss);
pa_assert(map); pa_assert(map);
pa_assert(nfrags);
pa_assert(period_size);
pa_assert(m); pa_assert(m);
try_ss.channels = m->channel_map.channels; try_ss.channels = m->channel_map.channels;
@ -524,8 +596,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
&try_ss, &try_ss,
&try_map, &try_map,
mode, mode,
nfrags,
period_size, period_size,
buffer_size,
tsched_size, tsched_size,
use_mmap, use_mmap,
use_tsched, use_tsched,
@ -547,8 +619,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_sample_spec *ss, pa_sample_spec *ss,
pa_channel_map* map, pa_channel_map* map,
int mode, int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, pa_bool_t *use_mmap,
pa_bool_t *use_tsched, pa_bool_t *use_tsched,
@ -579,7 +651,15 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_log_debug("Managed to open %s", d); pa_log_debug("Managed to open %s", d);
if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) { if ((err = pa_alsa_set_hw_params(
pcm_handle,
ss,
period_size,
buffer_size,
tsched_size,
use_mmap,
use_tsched,
require_exact_channel_number)) < 0) {
if (!reformat) { if (!reformat) {
reformat = TRUE; reformat = TRUE;
@ -632,8 +712,8 @@ snd_pcm_t *pa_alsa_open_by_template(
pa_sample_spec *ss, pa_sample_spec *ss,
pa_channel_map* map, pa_channel_map* map,
int mode, int mode,
uint32_t *nfrags,
snd_pcm_uframes_t *period_size, snd_pcm_uframes_t *period_size,
snd_pcm_uframes_t *buffer_size,
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, pa_bool_t *use_mmap,
pa_bool_t *use_tsched, pa_bool_t *use_tsched,
@ -653,8 +733,8 @@ snd_pcm_t *pa_alsa_open_by_template(
ss, ss,
map, map,
mode, mode,
nfrags,
period_size, period_size,
buffer_size,
tsched_size, tsched_size,
use_mmap, use_mmap,
use_tsched, use_tsched,

View file

@ -42,8 +42,8 @@
int pa_alsa_set_hw_params( int pa_alsa_set_hw_params(
snd_pcm_t *pcm_handle, snd_pcm_t *pcm_handle,
pa_sample_spec *ss, /* modified at return */ pa_sample_spec *ss, /* modified at return */
uint32_t *periods, /* modified at return */
snd_pcm_uframes_t *period_size, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */
@ -60,8 +60,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_auto(
pa_sample_spec *ss, /* modified at return */ pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */ pa_channel_map* map, /* modified at return */
int mode, int mode,
uint32_t *nfrags, /* modified at return */
snd_pcm_uframes_t *period_size, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */
@ -75,8 +75,8 @@ snd_pcm_t *pa_alsa_open_by_device_id_mapping(
pa_sample_spec *ss, /* modified at return */ pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */ pa_channel_map* map, /* modified at return */
int mode, int mode,
uint32_t *nfrags, /* modified at return */
snd_pcm_uframes_t *period_size, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */
@ -89,8 +89,8 @@ snd_pcm_t *pa_alsa_open_by_device_string(
pa_sample_spec *ss, /* modified at return */ pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */ pa_channel_map* map, /* modified at return */
int mode, int mode,
uint32_t *nfrags, /* modified at return */
snd_pcm_uframes_t *period_size, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */
@ -104,8 +104,8 @@ snd_pcm_t *pa_alsa_open_by_template(
pa_sample_spec *ss, /* modified at return */ pa_sample_spec *ss, /* modified at return */
pa_channel_map* map, /* modified at return */ pa_channel_map* map, /* modified at return */
int mode, int mode,
uint32_t *nfrags, /* modified at return */
snd_pcm_uframes_t *period_size, /* modified at return */ snd_pcm_uframes_t *period_size, /* modified at return */
snd_pcm_uframes_t *buffer_size, /* modified at return */
snd_pcm_uframes_t tsched_size, snd_pcm_uframes_t tsched_size,
pa_bool_t *use_mmap, /* modified at return */ pa_bool_t *use_mmap, /* modified at return */
pa_bool_t *use_tsched, /* modified at return */ pa_bool_t *use_tsched, /* modified at return */