ALSA sound cut short (gcc)

Hello all,

I'm trying to generate a simple beep, and am heavily basing it on an example found here:

formatting link
Listing 3. Simple Sound Playback

I've removed the reading from stdin and replaced it with filling the buffer with a simpel square wave.

The problem is that when I set a sample rate of 8000 (instead of 44100*) the sound only lasts for just less than a single second, instead of the in the code specified 5. I've checked both the "period time" (32 ms) as well as the "loop" count (156), and it all adds up to aprox. 5 sec.

*not sure if 44100 has the same problem, just that is was /very/ noticable using 8000.

Inserting a "snd_pcm_nonblock(handle,0)" before the "snd_pcm_drain(handle)" doesn't seem to change anything. However, putting a long-enough "usleep( )" before the above "snd_pcm_drain( )" does show that the written sound is indeed 5 seconds long (the beep ends some time before the time delta* code is executed and printed)

*times are retrieved using "gettimeofday( )".

Remark: moving the delay to /after/ "snd_pcm_drain( )" (to see if the "snd_pcm_close( )" was maybe to blame) still cuts the sound short. No idea why though.

Question: How do I make the program wait until the full length of the written sound has been played - without resorting to throwing some best-guess "usleep( )" hacks into into it I mean. In other words: what is missing from the example program I've been using ?

Regards, Rudy Wieser

Reply to
R.Wieser
Loading thread data ...

Hi Rudy,

I see you've not had any response to your query. I'm not surprised. I don't think there are many people who have programmed the ALSA interface, and you have probably found there are not many tutorials on how to use it.

20 years or more ago I wrote a set of linux programs to generate the sort of signals a traditional lab signal generator would do. This was before ALSA, the kernel sound system then being the Open Sound System (OSS). It proved fairly easy to use. When ALSA came along, I looked at converting the programs, but it is Soooo.... complicated :-( and back then there was even less tutorial or introductory articles. So I gave up. You just might have got me interested in trying again.

I hope you have made progress.

Jim

Reply to
Jim Jackson

Jim,

Indeed. :-\

Not ? In that case, how does everybody else get user-defined sounds from their speakers ? Is there another approach available ?

Indeed again. Some code snippets, a few "compile this" programs, and a list of pcm related functions. Thats about it.

The basic setup, as presented in that linke I provided, looks fairly simple to me. Its just that when I make small changes (like changing the sample rate) it refuses to work (change) in a way I can make sense of. :-\

So ? You will have the solution for me tomorrow ? :-D (yes, that was a joke. No, really :-) )

Just this morning I tried some super-simple code using "snd_pcm_set_params( )" and a single buffer (

formatting link
) and found out that by lowering with the latency parameter value (the last one) I could get the same "cut short" effect my origionally mentioned code suffers from.

That made me think that maybe the internal ALSA buffer empties before a new set of data (my programs buffer) is written into it ... Which is as far as I currently got.

Though no idea why, when I put a seconds long "usleep( )" before the "snd_pcm_drain( )", the whole sound is played without me hearing any kind of hickups.

And again, if you know of another, more dependable way to generate sounds in a C program than I would really like to know (as in a different API).

Regards, Rudy Wieser

P.s. I tried to set a bigger internal buffer (snd_pcm_hw_params_set_buffer_size_near( ) ), but could not get it to change from 32768.

Reply to
R.Wieser

ALSA is quite complicated at first. It's some time since I've worked on it, but this is what I found out.

It is a series of negotiations. You can't just say I want 2 channels at 16bit, for example. You make a request, and based on the hardware driver and ALSA's internal capabilities you are told what you've got.

Number of channels is a particular GOTCHA as it seems you'll usually get back whatever is available regardless of what you ask for so you have to watch out for buffer sizes, and channel numbers.

Also, 16bit usually seems to be done as interleaved short integers, which means you have to pair up adjacent channels (with one of the 16bit shifted).

HTH

--
W J G
Reply to
Folderol

Folderol,

[snip]

Setting up the whole thing doesn't seem to be creating any kind of problems.

And even though the code in the link I provided doesn't do a lot of error checking it does use the values returned by ALSA to both determine the size of the programs buffer as well as how many times it should send it.

Its just that I have zero idea why the sound gets cut short, even though inserting a seconds long usleep( ) just before the snd_pcm_drain( ) call shows that the whole length of sound is there.

Regards, Rudy Wieser

Reply to
R.Wieser

Does it return the same number of channels as you want to send? If not you need to work out *when* to actually send them!

Alternatively, suggests a periods V buffersize issue to me. I can't remember for sure, but I think ALSA runs 2 periods per buffer, so one half of the buffer is played while you send to the other half, so if your sample is longer than 1/2 buffer you'll lose the rest.

--
W J G
Reply to
Folderol

Folderol,

Yes, two.

Thats not what the code I linked to (in my initial message) seems to indicate.

And as far as I know "snd_pcm_writei( )" function will block until all frames are written (which some other, later found code (link also posted) seems to confirm)..

Besides, even if the code in the first link I've provided causes half of the frames to be dropped I should get two-and-a-half seconds worth (instead of five), not just shy of a single one. Apart from me assuming that the "snd_pcm_writei( )" will than either return a different number of accepted frames than what gets provided, or throw an error - both of which are catched and displayed.

Next to that, I /do/ get the full five seconds worth of writes (even though I write as many frames as returned by the "snd_pcm_hw_params_set_period_size_near( )" call) - as long as I preceede "snd_pcm_drain( )" with a few seconds of "usleep( )".

That is part of the problem: I can not make head-or-tails of how/when the cut-off happens. And no errors of any kind.

Regards, Rudy Wieser

Reply to
R.Wieser

On Fri, 3 Jan 2020 15:38:15 +0100, "R.Wieser" declaimed the following:

This description is very perplexing to me...

A usleep() before the snd_pcm_drain() implies that the OS is during something BEFORE you invoke the snd_pcm_drain() call. So focusing on that call is a red-herring. It indicates, to me, that some call before the usleep() needs to be investigated.

Something like an asynchronous buffering operation which is returning before the entire buffer has been transferred, and your subsequent operations are interrupting/terminating the remaining transfer.

Just some random Google results...

formatting link
formatting link

--
	Wulfraed                 Dennis Lee Bieber         AF6VN 
	wlfraed@ix.netcom.com    http://wlfraed.microdiversity.freeddns.org/
Reply to
Dennis Lee Bieber

They use existing software.

system("aplay somesound.wav");

Yes I know :-(

The other way is hardware. Use a spare gpio pin to switch on and off a ceramic sounder.

Jim

Reply to
Jim Jackson

Dennis,

I would hope so ! :-)

Why do you think so ? Because there is already sound before the snd_pcm_drain() is called ? That doesn't seem strange to me, as I assume it notices its internal buffer being totally filled up, and thus starts to play that.

I've posted the link to the code I've used, so please do tell which one you think is involved, as I'm at the end of my rope here (remember that I removed the reading from stdin and changed the bitrate to 8000). FWI, the last-called function before snd_pcm_drain() is snd_pcm_writei().

I've also just tried to make the buffer (much) larger and smaller (ofcourse adjusting the loopcount too), but the sound kept playing for just a second ...

To me it feels like the internal ALSA buffer goes empty (due to task switching ?), causing the snd_pcm_drain() function to end the sound output and return - ignoring/discarding the rest of the data that is still waiting to be played.

Found that one too. But as it uses an usleep() where none should be present and no explanation is given (that I can see) I skipped it.

Also found that one, and tried what happened when I included the "snd_pcm_nonblock(handle, 0)" call. Alas, nothing changed.

Found a few others too, but no explanation or solution in sight.

Regards, Rudy Wieser

Reply to
R.Wieser

so take a look at the source code for a play

--
But Officer, I stopped for the last one, and it was green!
Reply to
Alister

Jim,

...

You got it. :-)

Yeah, have been thinking of (something like) that too. Needs extra hardware though (not much, but not always available).

And there is ofcourse my own "if some program can do it, why can't I ?" grumbling. For some reason I now just have to try my damn best to battle ALSA to do my bidding.

Yeah, I know. Stubbornness. I'm cursed with it. :-( :-)

Regards, Rudy Wieser

Reply to
R.Wieser

Alister,

Like this one ?

formatting link

or this one ?

formatting link

Even though they might work flawlessly* , they are far from being a simple/clear examples. :-(

*I'm not even sure of that, as the example I got (link in first post) works great - using a sample rate of 44100 ...

Regards, Rudy Wieser

Reply to
R.Wieser

All four listings on that page work for me.

So I have to wonder if you've introduced a bug in your code changes which you have described but not shared.

You might have more luck in a more general Linux group (comp.os.linux.misc gets a fair amount of traffic) or a programming group (comp.lang.c is active). But include your actual code.

I used a wrapper around "aplay" for ALSA sound playback when I made an alarm clock. I fork a program that loops aplay for however long it takes to play the sound file for one minute, while the parent program checks the state of a GPIO input to detect silence button pushed. On push, it then kills the child process.

Elijah

------ requires GPIO in both states at least once to handle either NO or NC switches

Reply to
Eli the Bearded

If its any consolati I too tried some code under alsa. It (alsa)was badly documented and illogical. I just looked at it, but I dont even understand what I wrote and its late.

Maybe I'll dig it up and see if it bears any relationship. It did work but at 44k frame rate - never tried anything else.

--
The biggest threat to humanity comes from socialism, which has utterly  
diverted our attention away from what really matters to our existential  
survival, to indulging in navel gazing and faux moral investigations  
into what the world ought to be, whilst we fail utterly to deal with  
what it actually is.
Reply to
The Natural Philosopher

I agree the ALSA documentation is a mess - there are broken links in the documentation part of the website. the C library API documentation is a mess, but a quick glance shows that there seems to be some good stuff in:

formatting link
(describes the architecture and module map)

formatting link
(describes the config files and where to find them)

.. and the example programs are worth a look too.

--
Martin    | martin at 
Gregorie  | gregorie dot org
Reply to
Martin Gregorie

Eli,

Thats strange to say the least: In the code I posted the link to I had to initialize the "dir" variable (to zero) to get rid of an initial delay (in the "snd_pcm_hw_params_set_rate_near( )" function). Compiler differences ? (I'm using gcc)

Besides that, the code worked alright for me too. Up until I changed the sample rate from 44100 to 8000 that is. Than the sound became much shorter.

I just re-started with the code I took from the link I posted, and changed the "val = 44100;" line into val = 8000; dir = 0;

Thats all I need to get the (much) shorter sound.

And just now, after re-adding the code to measure the time from the start of the loop until after "snd_pcm_drain()", I noticed that even with the origional bitrate of 44100 the sound now only goes for 4.3 seconds ...

I have been thinking of that too, but I currently regard the problem as being ALSA, and possibly Raspberry Pi specific, not one of the language itself.

Regards, Rudy Wieser

Reply to
R.Wieser

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.