0
$\begingroup$

Using a pretty typical call to Linux's ALSA, snd_pcm_writei(pcm_handle, pcm_buffer, pcm_buffer_size) how do you know how long it should take to send this data? What is the formula?

The sound card should send frames (a frame is two samples for stereo, in ALSA terminology) at a certain rate, however, what is this rate? Sample rate? I don't understand how the sample rate relates to this.

If I have a sample rate of 44100, does it mean it sends 44100 samples per second? Given that my buffer in my program contains 8820 samples, I should see it drain this buffer 5 times every second (44100 / 8820 = 5). However, in reality I see it drain the buffer something like 10 times per second.

I could provide code, but I think it only would make the question less clear.

I've read https://stackoverflow.com/questions/24040672/the-meaning-of-period-in-alsa and www.linuxjournal.com/article/6735?page=0,1#N0x19ab2890.0x19ba78d8 as well as some other guides. However, it is still unclear to me how long it actually takes for the sound card to send data that it has received from snd_pcm_writei().

$\endgroup$
  • $\begingroup$ It probably varies with hardware and driver. Have you tried to measure it experimentally? Output something on the L channel (to trigger a scope), then call snd_pcm_writei(0 on the R channel, and measure the delay on the scope. $\endgroup$ – MBaz Feb 7 at 23:01
  • $\begingroup$ @MBaz I tried, but the hardware has a buffer, which eats it up immediately, so I can't measure the time inside of the program (the call returns immediately, then the sound relatively slowly plays on the speakers). External equipment, I tried that, but it isn't easy. I'd like to know in microseconds preferably, and milliseconds at the very least -- I actually need this accurately for what I'm trying to do. $\endgroup$ – deltafft Feb 7 at 23:03
  • $\begingroup$ What I'm proposing is measuring the actual sound outputs from the sound card, not from software. And scopes have absolutely no problem measuring microseconds. $\endgroup$ – MBaz Feb 7 at 23:05
  • $\begingroup$ @MBaz I understand, I don't have a scope though, and I need to do this computationally, because my buffer size and sample rate might vary. I can't use a scope to empirically arrive at these numbers. $\endgroup$ – deltafft Feb 7 at 23:06
0
$\begingroup$

Please correct me if I'm wrong.

snd_pcm_hw_params_get_period_size() gives the number of times per second a chunk of data is sent, i.e. in Hz.

snd_pcm_hw_params_get_period_time() gives the number of microseconds to send such a chunk.

E.g. for my example, it takes in reality around 10 times per second, and these calls return to me 4410 Hz and 10000 usec, respectively. Hence, this appears correct.

However, it still eludes me how the buffer size plays into this. If anyone who happens to be experienced with ALSA would like to add that, that would be awesome.

Edit:

I believe it works like this:

In the same example, 4410 frames will be required for each period. This makes it 8820 samples. If you're using S16_LE, i.e. short data types, then each frame is 2 byte, hence the buffer should be 17640 bytes.

So, that is the buffer which is treated during each period.

However, there is another important concept in ALSA, the hardware buffer, which can be quite large and introduce a latency when copying it---Hence why ALSA uses the concept of periods at all.

Reference: https://www.linuxjournal.com/article/6735

" The size of the buffer can be programmed by ALSA library calls. The buffer can be quite large, and transferring it in one operation could result in unacceptable delays, called latency. To solve this, ALSA splits the buffer up into a series of periods (called fragments in OSS/Free) and transfers the data in units of a period.

A period stores frames, each of which contains the samples captured at one point in time. For a stereo device, the frame would contain samples for two channels. Figure 1 illustrates the breakdown of a buffer into periods, frames and samples with some hypothetical values. Here, left and right channel information is stored alternately within a frame; this is called interleaved mode. A non-interleaved mode, where all the sample data for one channel is stored followed by the data for the next channel, also is supported. "

|improve this answer|||||
$\endgroup$
0
$\begingroup$

ALSA breaks audio buffers into periods. One buffer has integer number of periods.

The I2S silicon automatically streams out from or in to your audio buffers using DMA hardware. Typically the DMA size is scaled to an ALSA period size so that part of the audio subsystem is at least double buffered. Which means you are working on one buffer while the other one is under DMA.

You will also find that you don't get exactly on time callbacks or access to period buffers. This is because the operating system scheduler will chew up some time in waking your thread for execution. There are ways to improve this overhead by raising the priority of your computational audio thread. See this code for example.

There are also playback examples and capture examples in that code which you may like as a reference, however you may already have plenty of references sorted out.

Finally there is a thread on using this C++ ALSA based code, if you would like to try it out here.

|improve this answer|||||
New contributor
Matt is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.