Correctly sizing Alsa buffers, weird API
Asked Answered
C

3

7

I'm currently working on a project that requires me to do some sampling with Alsa. I'm trying to configure correctly everything but I'm stuck on how to correctly size my reading.

There are two primitives that seem to be interesting for my task:

snd_pcm_hw_params_get_period_time
snd_pcm_hw_params_get_buffer_size

The name of the first one suggests that the output will be the time length of a sampling period, however that's weird: if I'm setting the sampling rate on f = 44100Hz the sampling period (in nanoseconds) should be T0 = 1e9 / 44100 ~= 22676 ns while the function will answer T1 = 725 us = 725000 ns.

Meanwhile, even if I've been asked to use non-locking primitives, I'm trying to profile the time required for locking 'readi', and it turns out that the sample requires T2 = 8028603 ns in the best case time and T3 = 12436217 ns in the worst case.

Finally I can't figure out what's the meaning of the following two:

snd_pcm_hw_params_get_buffer_time
snd_pcm_hw_params_get_period_size

I don't get how could I measure the buffer in time and the period in size, However the former returns the same value as get_buffer_size, while the latter returns the same value as get_period_time.

Any hint?

Corporate answered 27/7, 2010 at 15:14 Comment(0)
C
5

This is my soundcard initialization function.

First of all I set needed parameters

static
int init_soundcard (snd_pcm_t *handle, unsigned *rate, uint8_t channels,
                    snd_pcm_uframes_t *nframes, unsigned *period)
{
    snd_pcm_hw_params_t *hwparams;
    int err;

    snd_pcm_hw_params_alloca(&hwparams);

    err = snd_pcm_hw_params_any(handle, hwparams);
    if (err < 0) return err;

    err = snd_pcm_hw_params_set_rate_near(handle, hwparams, rate, NULL);
    if (err < 0) return err;

    err = snd_pcm_hw_params_set_access(handle, hwparams,
                                       SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err < 0) return err;

    err = snd_pcm_hw_params_set_format(handle, hwparams,
                                       SND_PCM_FORMAT_S16_LE);
    if (err < 0) return err;

    err = snd_pcm_hw_params_set_channels(handle, hwparams, channels);
    if (err < 0) return err;

When every parameter has been setted correctly, the parameters are applied to the handle:

    err = snd_pcm_hw_params(handle, hwparams);
    if (err < 0) return err;

After it has been applied the brave programmer can obtain the required data as follows:

get_period_size_min() gives the minimum size in frames of the buffer that will contain the sampling. A buffer having this size is wide enough.

    err = snd_pcm_hw_params_get_period_size_min(hwparams, nframes, NULL);
    if (err < 0) return err;

This is pretty counter-intuitive, but the correct sampling period is not given by 1/rate as one may think. One can obtain the sampling period by using the get_period_time() primitive!

    err = snd_pcm_hw_params_get_period_time(hwparams, period, NULL);
    if (err < 0) return err;

    return 0;
}
Corporate answered 6/8, 2010 at 8:30 Comment(0)
E
7

ALSA has some weird^Wspecific terminology:

  • Frames: samples x channels (i.e: stereo frames are composed of two samples, mono frames are composed of 1 sample,...)
  • Period: Number of samples tranferred after which the device acknowledges the transfer to the apllication (usually via an interrupt).

The *_size functions appear to return sizes in frames.

HTH

Ecclesiastes answered 27/7, 2010 at 15:32 Comment(3)
What they return is clear by the return type, which is snd_uframe_t for *_size and unsigned for *_time, but I don't get what's the meaning of "buffer time" and "period size".Corporate
Period size: val Returned approximate period size in frames Buffer time (of course it depends on current configuration): val Returned buffer time in usEcclesiastes
Yes, the documentation has the same informative content of the function name, but what is a buffer time? The buffer has a certain size, not a time.Corporate
C
5

This is my soundcard initialization function.

First of all I set needed parameters

static
int init_soundcard (snd_pcm_t *handle, unsigned *rate, uint8_t channels,
                    snd_pcm_uframes_t *nframes, unsigned *period)
{
    snd_pcm_hw_params_t *hwparams;
    int err;

    snd_pcm_hw_params_alloca(&hwparams);

    err = snd_pcm_hw_params_any(handle, hwparams);
    if (err < 0) return err;

    err = snd_pcm_hw_params_set_rate_near(handle, hwparams, rate, NULL);
    if (err < 0) return err;

    err = snd_pcm_hw_params_set_access(handle, hwparams,
                                       SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err < 0) return err;

    err = snd_pcm_hw_params_set_format(handle, hwparams,
                                       SND_PCM_FORMAT_S16_LE);
    if (err < 0) return err;

    err = snd_pcm_hw_params_set_channels(handle, hwparams, channels);
    if (err < 0) return err;

When every parameter has been setted correctly, the parameters are applied to the handle:

    err = snd_pcm_hw_params(handle, hwparams);
    if (err < 0) return err;

After it has been applied the brave programmer can obtain the required data as follows:

get_period_size_min() gives the minimum size in frames of the buffer that will contain the sampling. A buffer having this size is wide enough.

    err = snd_pcm_hw_params_get_period_size_min(hwparams, nframes, NULL);
    if (err < 0) return err;

This is pretty counter-intuitive, but the correct sampling period is not given by 1/rate as one may think. One can obtain the sampling period by using the get_period_time() primitive!

    err = snd_pcm_hw_params_get_period_time(hwparams, period, NULL);
    if (err < 0) return err;

    return 0;
}
Corporate answered 6/8, 2010 at 8:30 Comment(0)
P
2

From what I understood using and reading the ALSA library, this period is related to the hardware interruptions being made. If you use

snd_pcm_hw_params_get_period_size()

It will return you the number of frames passed to the hw by interruption. Similarly, if you use

snd_pcm_hw_params_get_period_time()

You will get the time in us (10^-6) that those frames will be inserted. So the "real" sampling rate can be obtained if

snd_pcm_hw_params_get_period_size()*1000000/snd_pcm_hw_params_get_period_time()
Priscella answered 28/6, 2016 at 14:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.