All of lore.kernel.org
 help / color / mirror / Atom feed
* [BUG?] Setting (start_threshold > stop_threshold) makes snd_pcm_writei(a_small_buffer) XRUN immediately
@ 2021-10-01 17:31 Zhang Boyang
  2021-10-01 23:33 ` Geraldo Nascimento
  0 siblings, 1 reply; 6+ messages in thread
From: Zhang Boyang @ 2021-10-01 17:31 UTC (permalink / raw)
  To: alsa-devel

[-- Attachment #1: Type: text/plain, Size: 808 bytes --]

Hello,

   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.
   Thank you very much!

Zhang Boyang

p.s.
   I dug into kernel code. After writting hardware buffer, 
__snd_pcm_lib_xfer() called snd_pcm_update_state(), which set the XRUN 
state.

[-- Attachment #2: start_stop_threshold_bug.c --]
[-- Type: text/x-csrc, Size: 4494 bytes --]

#define PCM_DEVICE "hw:1,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;
	char *buff;
	int buff_size, loops;


	rate 	 = 44100;
	channels = 2;
	seconds  = 10;
	
	int start_threshold = 100;
	int stop_threshold = 50;

	/* 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));

	/* Allocate parameters object and fill it with default values*/
	snd_pcm_hw_params_alloca(&params);

	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));

	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));

	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));

	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));

	/* Write parameters */
	if ((pcm = snd_pcm_hw_params(pcm_handle, params) < 0))
		printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm));

    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));
		return pcm;
	}
	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));
		return pcm;
	}
	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));
		return pcm;
	}
	pcm = snd_pcm_sw_params(pcm_handle, swparams);
	if (pcm < 0) {
		printf("Unable to set sw params: %s", snd_strerror(pcm));
		return pcm;
	}
	
	/* 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 bps\n", tmp);

	printf("seconds: %d\n", seconds);	


    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;
}

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [BUG?] Setting (start_threshold > stop_threshold) makes snd_pcm_writei(a_small_buffer) XRUN immediately
  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
  0 siblings, 1 reply; 6+ messages in thread
From: Geraldo Nascimento @ 2021-10-01 23:33 UTC (permalink / raw)
  To: Zhang Boyang; +Cc: alsa-devel

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.

Thanks!
Geraldo Nascimento

>    Thank you very much!
> 
> Zhang Boyang
> 
> p.s.
>    I dug into kernel code. After writting hardware buffer, 
> __snd_pcm_lib_xfer() called snd_pcm_update_state(), which set the XRUN 
> state.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [BUG?] Setting (start_threshold > stop_threshold) makes snd_pcm_writei(a_small_buffer) XRUN immediately
  2021-10-01 23:33 ` Geraldo Nascimento
@ 2021-10-02  1:01   ` Geraldo Nascimento
  2021-10-02  4:45     ` Geraldo Nascimento
  0 siblings, 1 reply; 6+ messages in thread
From: Geraldo Nascimento @ 2021-10-02  1:01 UTC (permalink / raw)
  To: Zhang Boyang; +Cc: alsa-devel

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

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [BUG?] Setting (start_threshold > stop_threshold) makes snd_pcm_writei(a_small_buffer) XRUN immediately
  2021-10-02  1:01   ` Geraldo Nascimento
@ 2021-10-02  4:45     ` Geraldo Nascimento
  2021-10-04  9:48       ` Zhang Boyang
  0 siblings, 1 reply; 6+ messages in thread
From: Geraldo Nascimento @ 2021-10-02  4:45 UTC (permalink / raw)
  To: Zhang Boyang; +Cc: alsa-devel

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



^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [BUG?] Setting (start_threshold > stop_threshold) makes snd_pcm_writei(a_small_buffer) XRUN immediately
  2021-10-02  4:45     ` Geraldo Nascimento
@ 2021-10-04  9:48       ` Zhang Boyang
  2021-10-04 21:24         ` Geraldo Nascimento
  0 siblings, 1 reply; 6+ messages in thread
From: Zhang Boyang @ 2021-10-04  9:48 UTC (permalink / raw)
  To: Geraldo Nascimento; +Cc: alsa-devel

[-- 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(&params);

    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;
}

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [BUG?] Setting (start_threshold > stop_threshold) makes snd_pcm_writei(a_small_buffer) XRUN immediately
  2021-10-04  9:48       ` Zhang Boyang
@ 2021-10-04 21:24         ` Geraldo Nascimento
  0 siblings, 0 replies; 6+ messages in thread
From: Geraldo Nascimento @ 2021-10-04 21:24 UTC (permalink / raw)
  To: Zhang Boyang; +Cc: alsa-devel

On Mon, Oct 04, 2021 at 05:48:49PM +0800, Zhang Boyang wrote:
> 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,

Hey there, Zhang,

> 
> Thank you for your reply.

It was my pleasure.

> 
> 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).

Sure it's the relationship.

If you insist on using (bufsz-50) you will get those instant XRUNs
forever.

Try using ((bufsz - frames) + 1) and enlarge your start_threshold
to at least 264. Your original code works for me with such values but
your mileage may vary. If you get XRUNs, play with the threshold values,
enlarging stop threshold and/or enlarging start threshold.

> 
> 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.

Clever. Reminds me of the workings of the new low latency mode recently
introduced for snd-usb-audio. I haven't been able to test your revised
code but that's certainly something you'd like to keep in your codebase,
to deal nicely with eventual XRUNs.

Thank you,
Geraldo Nascimento

> 
> Anyway, Thank you for your advice. Have a nice day. :)
> 
> Zhang Boyang


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-10-04 21:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
2021-10-04 21:24         ` Geraldo Nascimento

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.