From: Zhang Boyang <zhangboyang.id@gmail.com>
To: Geraldo Nascimento <geraldogabriel@gmail.com>
Cc: alsa-devel@alsa-project.org
Subject: Re: [BUG?] Setting (start_threshold > stop_threshold) makes snd_pcm_writei(a_small_buffer) XRUN immediately
Date: Mon, 4 Oct 2021 17:48:49 +0800 [thread overview]
Message-ID: <9bd7f88a-d2a3-b064-2ca7-7305012ef679@gmail.com> (raw)
In-Reply-To: <YVfj7SA4uhYKWTH6@geday>
[-- Attachment #1: Type: text/plain, Size: 3395 bytes --]
On 2021/10/2 12:45, Geraldo Nascimento wrote:
> On Fri, Oct 01, 2021 at 10:01:34PM -0300, Geraldo Nascimento wrote:
>> On Fri, Oct 01, 2021 at 08:33:23PM -0300, Geraldo Nascimento wrote:
>>> On Sat, Oct 02, 2021 at 01:31:12AM +0800, Zhang Boyang wrote:
>>>> Hello,
>>>
>>> Hello, Zhang!
>>>
>>>>
>>>> I'm using ALSA to develop an audio-streaming application. I try to
>>>> use start_threshold and stop_threshold in combination with small
>>>> buffers. However, I think I probably found a bug about this.
>>>> I'm setting start_threshold=100 and stop_threshold=50. I'm also using
>>>> a buffer of 44 frames. When I call
>>>> snd_pcm_writei(the_small_44_frames_buffer), pcm state came to XRUN from
>>>> PREPARED directly. I think this is a bug because the stream hasn't
>>>> started. It's hard to say a xrun occurred while stream not started.
>>>> I'm wondering if this is a ALSA-bug or a misuse of ALSA. A simple bug
>>>> test program is attached.
>>>
>>> No, I don't think it's a bug. You're bound to run into problems with a
>>> period size of only 44 frames.
>>>
>>> Moreover, working with the code you provided, I was able to get a RUNNING
>>> state without XRUNs with a period size of 4410 frames (100 milliseconds of
>>> audio) but I had to comment out snd_pcm_sw_params_set_stop_threshold() for
>>> it to work or I'd have those instant XRUNs.
>>>
>>> That's how snd_pcm_sw_params_set_stop_threshold() is supposed to work by
>>> the way. It creates a XRUN once the threshold is hit.
>>
>> Oh, and snd_pcm_sw_params_set_stop_threshold() is a little
>> counter-intuitive. The threshold you give the function is compared to
>> the available free space in the ring buffer, not the filled space.
>>
>> That's why lowering the stop threshold makes XRUNs more likely...
>>
>> Thanks,
>> Geraldo Nascimento
>
> Zhang,
>
> I was able to make your code work in the end, even with 44 frames
> writes, which is unadvisable since it eats too much CPU specially if
> they are low-end like the ARM64 box I'm writing you from.
>
> The trick was to use high values such as 500000. Lower values such as
> 250000 were enough to trigger XRUNs in my machine. Of course you can set
> it to -1 and it will ignore XRUNs but that's a bit like setting it to
> 16777216 or 4096 * 4096 which is a hardcoded limit in alsa-lib, I
> believe.
>
> And plainly ignoring XRUNs may sound bad under load, believe me.
>
> Thank you,
> Geraldo Nascimento
>
>
Hi,
Thank you for your reply.
I realized my mistake about the meaning of stop_threshold. The
stop_threshold should be (bufsz-50), where (bufsz = alsa buffer size). I
fixed my code and new code is attached.
However, the problem still exists. I'm trying to minimize audio latency,
so I must use a small buffer. In fact, the problem is not related to the
absoulte size of buffer, but the relationship between the size of
start_threshold, stop_threshold and the buffer. The problem still exists
even using manual start mode (i.e. start_threshold>bufsz).
Currently, I'm using a workaround. After PREPARED or XRUN'd, I manually
buffer incoming audio frames, and when (n_buffered_frames >=
start_threshold && n_buffered_frames >= bufsz-stop_threshold), I write
them in a single write() call. Note that the write must in whole, any
short write may result in immediate XRUN.
Anyway, Thank you for your advice. Have a nice day. :)
Zhang Boyang
[-- Attachment #2: start_stop_threshold_bug.c --]
[-- Type: text/x-csrc, Size: 5350 bytes --]
#define PCM_DEVICE "hw:0,0"
/*
gcc -Wall -o bug start_stop_threshold_bug.c -lasound
./bug
*/
/*
* Simple sound playback using ALSA API and libasound.
*
* Compile:
* $ cc -o play sound_playback.c -lasound
*
* Usage:
* $ ./play <sample_rate> <channels> <seconds> < <file>
*
* Examples:
* $ ./play 44100 2 5 < /dev/urandom
* $ ./play 22050 1 8 < /path/to/file.wav
*
* Copyright (C) 2009 Alessandro Ghedini <al3xbio@gmail.com>
* --------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* Alessandro Ghedini wrote this file. As long as you retain this
* notice you can do whatever you want with this stuff. If we
* meet some day, and you think this stuff is worth it, you can
* buy me a beer in return.
* --------------------------------------------------------------
*/
#include <alsa/asoundlib.h>
#include <stdio.h>
int main(int argc, char **argv) {
unsigned int pcm, tmp;
unsigned rate;
int channels, seconds;
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames, bufsz;
char *buff;
int buff_size, loops;
rate = 44100;
channels = 2;
seconds = 10;
/* Open the PCM device in playback mode */
if ((pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE,
SND_PCM_STREAM_PLAYBACK, 0) < 0)) {
printf("ERROR: Can't open \"%s\" PCM device. %s\n",
PCM_DEVICE, snd_strerror(pcm));
abort();
}
/* Allocate parameters object and fill it with default values*/
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm_handle, params);
/* Set parameters */
snd_pcm_hw_params_set_rate_resample(pcm_handle, params, 0);
if ((pcm = snd_pcm_hw_params_set_access(pcm_handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED) < 0)) {
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm));
abort();
}
if ((pcm = snd_pcm_hw_params_set_format(pcm_handle, params,
SND_PCM_FORMAT_S16_LE) < 0)) {
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm));
abort();
}
if ((pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels) < 0)) {
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm));
abort();
}
if ((pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0)) {
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm));
abort();
}
/* Write parameters */
if ((pcm = snd_pcm_hw_params(pcm_handle, params) < 0)) {
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));
abort();
}
if ((pcm = snd_pcm_hw_params_get_buffer_size(params, &bufsz) < 0)) {
printf("ERROR: Can't get buffer size. %s\n", snd_strerror(pcm));
abort();
}
int start_threshold = 100;
int stop_threshold = bufsz - 50;
snd_pcm_sw_params_t *swparams;
snd_pcm_sw_params_alloca(&swparams);
pcm = snd_pcm_sw_params_current(pcm_handle, swparams);
if (pcm < 0) {
printf("Unable to determine current swparams: %s", snd_strerror(pcm));
abort();
}
pcm = snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, start_threshold);
if (pcm < 0) {
printf("Unable to set start threshold mode: %s", snd_strerror(pcm));
abort();
}
pcm = snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, stop_threshold);
if (pcm < 0) {
printf("Unable to set stop threshold mode: %s", snd_strerror(pcm));
abort();
}
pcm = snd_pcm_sw_params(pcm_handle, swparams);
if (pcm < 0) {
printf("Unable to set sw params: %s", snd_strerror(pcm));
abort();
}
/* Resume information */
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle));
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
snd_pcm_hw_params_get_channels(params, &tmp);
printf("channels: %i ", tmp);
if (tmp == 1)
printf("(mono)\n");
else if (tmp == 2)
printf("(stereo)\n");
snd_pcm_hw_params_get_rate(params, &tmp, 0);
printf("rate: %d\n", tmp);
printf("seconds: %d\n", seconds);
printf("bufsz: %ld\n", bufsz);
printf("start_threshold: %d\n", start_threshold);
printf("stop_threshold: %d\n", stop_threshold);
frames = 44;
buff_size = frames * channels * 2 /* 2 -> sample size */;
buff = (char *) malloc(buff_size);
memset(buff, 0, buff_size);
snd_pcm_hw_params_get_period_time(params, &tmp, NULL);
for (loops = (seconds * 1000000) / tmp; loops > 0; loops--) {
printf("==== loop ====\n");
printf("(before write) PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
pcm = snd_pcm_writei(pcm_handle, buff, frames);
printf("written %d\n", pcm);
printf("(after write) PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle)));
if (pcm == -EPIPE) {
printf("XRUN.\n");
snd_pcm_prepare(pcm_handle);
abort();
} else if (pcm < 0) {
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm));
}
}
snd_pcm_drain(pcm_handle);
snd_pcm_close(pcm_handle);
free(buff);
return 0;
}
next prev parent reply other threads:[~2021-10-04 9:49 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-10-01 17:31 [BUG?] Setting (start_threshold > stop_threshold) makes snd_pcm_writei(a_small_buffer) XRUN immediately Zhang Boyang
2021-10-01 23:33 ` Geraldo Nascimento
2021-10-02 1:01 ` Geraldo Nascimento
2021-10-02 4:45 ` Geraldo Nascimento
2021-10-04 9:48 ` Zhang Boyang [this message]
2021-10-04 21:24 ` Geraldo Nascimento
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=9bd7f88a-d2a3-b064-2ca7-7305012ef679@gmail.com \
--to=zhangboyang.id@gmail.com \
--cc=alsa-devel@alsa-project.org \
--cc=geraldogabriel@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox