How to use ALSA's snd_pcm_writei()?
Asked Answered
R

4

6

Can someone explain how snd_pcm_writei

snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer,
                                 snd_pcm_uframes_t size)

works?

I have used it like so:

for (int i = 0; i < 1; i++) {
   f = snd_pcm_writei(handle, buffer, frames);

   ...
}

Full source code at http://pastebin.com/m2f28b578

Does this mean, that I shouldn't give snd_pcm_writei() the number of all the frames in buffer, but only

sample_rate * latency = frames

?

So if I e.g. have: sample_rate = 44100 latency = 0.5 [s] all_frames = 100000

The number of frames that I should give to snd_pcm_writei() would be

sample_rate * latency = frames 44100*0.5 = 22050

and the number of iterations the for-loop should be?:

(int) 100000/22050 = 4; with frames=22050

and one extra, but only with

100000 mod 22050 = 11800

frames?

Is that how it works?

Louise

http://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html#gf13067c0ebde29118ca05af76e5b17a9

Rawden answered 1/2, 2010 at 23:43 Comment(0)
M
9

frames should be the number of frames (samples) you want to write from the buffer. Your system's sound driver will start transferring those samples to the sound card right away, and they will be played at a constant rate.

The latency is introduced in several places. There's latency from the data buffered by the driver while waiting to be transferred to the card. There's at least one buffer full of data that's being transferred to the card at any given moment, and there's buffering on the application side, which is what you seem to be concerned about.

To reduce latency on the application side you need to write the smallest buffer that will work for you. If your application performs a DSP task, that's typically one window's worth of data.

There's no advantage in writing small buffers in a loop - just go ahead and write everything in one go - but there's an important point to understand: to minimize latency, your application should write to the driver no faster than the driver is writing data to the sound card, or you'll end up piling up more data and accumulating more and more latency.

For a design that makes producing data in lockstep with the sound driver relatively easy, look at jack (http://jackaudio.org/) which is based on registering a callback function with the sound playback engine. In fact, you're probably just better off using jack instead of trying to do it yourself if you're really concerned about latency.

Macegan answered 2/2, 2010 at 15:14 Comment(4)
I only need to play ~17000 frames, so latency on the application side, would be okay. The strangest thing is, if I give snd_pcm_writei() all the frames, and remove the for-loop, nothing is played. If I keep the for-loop and e.g. set it to 5 iterations, it loops through the ~17000 frames twice. If I look in libsndfile's example at line 11, pastebin.com/m559397b3, he steps through the buffer. When I do that, nothing is played. Also what I find strange is that, snd_pcm_writei() always returns the same number of frames I give it. It never returns a lower number, which I would expect. ?Rawden
17000 samples is less than one second of playback. By writing the buffer repeatedly to the sound driver you're basically playing it again and again, but from your description it sounds as if something on your system prevents the beginning of the sound from playing. First, stepping through the buffer is the right thing to do; you don't need to write a sample more than once. Second, to see whether your sound driver or sound card fails to play the beginning of the sound, try padding your buffer with about a second's worth of 0 samples at the beginning, and see whether thatt makes a difference.Macegan
Thank you so much for clearing all this out. Now when you mention it, I do have some problems with audio when playing ALSA in e.g. MPlayer. The first 2 seconds or so nothing is played, and then it plays. I'll try and upgrade to Fedora 12, and then try again. Thanks again =)Rawden
I'd like to add that frames equals number_of_samples / channels.Quintessa
A
6

I think the reason for the "premature" device closure is that you need to call snd_pcm_drain(handle); prior to snd_pcm_close(handle); to ensure that all data is played before the device is closed.

Adze answered 7/3, 2013 at 20:26 Comment(0)
I
4

I did some testing to determine why snd_pcm_writei() didn't seem to work for me using several examples I found in the ALSA tutorials and what I concluded was that the simple examples were doing a snd_pcm_close () before the sound device could play the complete stream sent it to it.

I set the rate to 11025, used a 128 byte random buffer, and for looped snd_pcm_writei() for 11025/128 for each second of sound. Two seconds required 86*2 calls snd_pcm_write() to get two seconds of sound.

In order to give the device sufficient time to convert the data to audio, I put used a for loop after the snd_pcm_writei() loop to delay execution of the snd_pcm_close() function.

After testing, I had to conclude that the sample code didn't supply enough samples to overcome the device latency before the snd_pcm_close function was called which implies that the close function has less latency than the snd_pcm_write() function.

Incorporator answered 18/11, 2012 at 8:15 Comment(0)
B
1

If the ALSA driver's start threshold is not set properly (if in your case it is about 2s), then you will need to call snd_pcm_start() to start the data rendering immediately after snd_pcm_writei(). Or you may set appropriate threshold in the SW params of ALSA device.

ref:

Bonne answered 21/9, 2014 at 17:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.