[alsa-devel] how to handle buffer underrun in a softsynth
I have written a software synthesizer in Haskell that fetches MIDI events from ALSA sequencer and plays the rendered signal via ALSA pcm. However it cannot cope correctly with buffer underruns.
I tried to learn the correct way from the miniFMsynth example of http://alsamodular.sourceforge.net/alsa_programming_howto.html however this program floods my terminal with buffer underrun messages without even being connected to any MIDI event source. This is independent from how large I choose the BUFSIZE constant.
I use:
$ pkg-config --modversion alsa 1.0.14
Also, as far as I understand, that program allows to start notes only at block boundaries. This way it can ignore the timestamps of the incoming events. But this is crucial for me: I want to allow large blocksizes and thus I must allow notes to start anywhere inside a block. Now when I detect a buffer underrun on writing a block to an ALSA pcm sink, I like to know the time gap in order to adjust the timestamps of the incoming MIDI events.
My question in short is: Is there an example software synthesizer as simple as miniFMsynth, that works (on my machine), supports note start within a block and copes with buffer underruns in a reasonable way? If no working example, is there some documentation on how this is intended to work?
Thank you for your help Henning
Henning Thielemann wrote:
I have written a software synthesizer in Haskell that fetches MIDI events from ALSA sequencer and plays the rendered signal via ALSA pcm. However it cannot cope correctly with buffer underruns. ... Now when I detect a buffer underrun on writing a block to an ALSA pcm sink, I like to know the time gap in order to adjust the timestamps of the incoming MIDI events.
The sample clock is usually not synchronized with any other clock, so you won't be able to get a very exact measurement.
However, there is a different way to approach this problem: just disable underruns. :) Set the stop_threshold to the same value as the boundary, and ALSA will no longer stop the device when an underrun happens.¹ This means that you'll have to write some data more quickly than normally to catch up², but the overall timing of the samples will not be affected.
¹ The device will still stop on fatal errors such as an unplugged USB device. ² Or, if you detect this case, just use snd_pcm_forward(); the samples that are 'too late' won't be played anyway.
Regards, Clemens
On Mon, 28 Nov 2011, Clemens Ladisch wrote:
Henning Thielemann wrote:
I have written a software synthesizer in Haskell that fetches MIDI events from ALSA sequencer and plays the rendered signal via ALSA pcm. However it cannot cope correctly with buffer underruns. ... Now when I detect a buffer underrun on writing a block to an ALSA pcm sink, I like to know the time gap in order to adjust the timestamps of the incoming MIDI events.
The sample clock is usually not synchronized with any other clock, so you won't be able to get a very exact measurement.
That's something I worry about, too. However I must somehow map timestamps of ALSA sequencer events to sample counts in order to let notes start within a sample block. JACK claims to synchronize everything and it runs on top of ALSA - how does it manage synchronization between MIDI and PCM?
However, there is a different way to approach this problem: just disable underruns. :) Set the stop_threshold to the same value as the boundary, and ALSA will no longer stop the device when an underrun happens.¹ This means that you'll have to write some data more quickly than normally to catch up², but the overall timing of the samples will not be affected.
Ok, I am still uncertain whether this solves my problem. I think about how I can decrease processor load if I detect a buffer underrun. My idea was the following: Say, I have a blocksize of 10ms and got some events A, B, C, ... from the ALSA sequencer:
1ms: A, 7ms: B, 13ms: C, 19ms: D, 25ms: E, 31ms: F
Now, when I encounter a buffer underrun, I want instead to render according to:
1ms: A, 7ms: B, 10ms: C, 10ms: D, 15ms: E, 21ms: F
and play a 10ms pause at timestamp 10ms. That is I do not want to drop events (this might introduce inconsistencies) but I could save rendering a block and this way catch up with the emission of sample blocks. How can I detect a buffer underrun when I disabled underrun detection?
Regards, Henning
Henning Thielemann wrote:
I must somehow map timestamps of ALSA sequencer events to sample counts in order to let notes start within a sample block. JACK claims to synchronize everything and it runs on top of ALSA - how does it manage synchronization between MIDI and PCM?
It takes timestamps whenever ALSA reports a period boundary. (This requires that the code doesn't run too late, i.e., that underruns do not happen.) There may be some DLL to smooth out the measurements.
The timestamps can be made more reliable by using a recent enough kernel and alsa-lib, and enabling timestamps for PCM pointer updates with snd_pcm_sw_params_set_tstamp_mode(); then snd_pcm_status() will return both the buffer position and the corresponding timestamp.
How can I detect a buffer underrun when I disabled underrun detection?
The avail value is larger than the buffer size.
Regards, Clemens
participants (2)
-
Clemens Ladisch
-
Henning Thielemann