[alsa-devel] [PATCH - JACK plugin 1/4] jack: Do not Xrun the ALSA buffer
Takashi Iwai
tiwai at suse.de
Thu Mar 1 16:24:48 CET 2018
On Thu, 01 Mar 2018 14:14:05 +0100,
<twischer at de.adit-jv.com> wrote:
>
> From: Timo Wischer <twischer at de.adit-jv.com>
>
> when the JACK thread is requesting too many audio frames
>
> Playback:
> Without this commit the ALSA audio buffer
> will be played with endless repeats as long as the user application
> has not provided new audio data.
> Therefore this garbage will be played as long as the user application
> has not called snd_pcm_stop() after an Xrun.
> With this fix the rest of the JACK buffer will be filled
> with silence.
>
> Capture:
> Without this commit the audio data in the ALSA buffer would be
> overwritten.
> With this commit the new data from the JACK buffer
> will not be copied.
> Therefore the existing data in the ALSA buffer
> will not be overwritten.
Please try to format the text a bit nicer. These line breaks appear
like you're writing a poem :)
Now about the code changes:
> +static inline snd_pcm_uframes_t snd_pcm_jack_playback_avail(const snd_pcm_ioplug_t* const io,
> + const snd_pcm_uframes_t hw_ptr,
> + const snd_pcm_uframes_t appl_ptr)
You don't need to put inline unless it's really mandatory.
> +{
> + const snd_pcm_jack_t* const jack = io->private_data;
> +
> + /* cannot use io->hw_ptr without calling snd_pcm_avail_update()
> + * because it is not guranteed that snd_pcm_jack_pointer() was already
> + * called
> + */
> + snd_pcm_sframes_t avail;
> + avail = hw_ptr + io->buffer_size - appl_ptr;
> + if (avail < 0)
> + avail += jack->boundary;
> + else if ((snd_pcm_uframes_t) avail >= jack->boundary)
> + avail -= jack->boundary;
> + return avail;
> +}
> +
> +static inline snd_pcm_uframes_t snd_pcm_jack_capture_avail(const snd_pcm_ioplug_t* const io,
> + const snd_pcm_uframes_t hw_ptr,
> + const snd_pcm_uframes_t appl_ptr)
> +{
> + const snd_pcm_jack_t* const jack = io->private_data;
> +
> + /* cannot use io->hw_ptr without calling snd_pcm_avail_update()
> + * because it is not guranteed that snd_pcm_jack_pointer() was already
> + * called
> + */
> + snd_pcm_sframes_t avail;
> + avail = hw_ptr - appl_ptr;
> + if (avail < 0)
> + avail += jack->boundary;
> + return avail;
> +}
> +
> +static inline snd_pcm_uframes_t snd_pcm_jack_avail(const snd_pcm_ioplug_t* const io,
> + const snd_pcm_uframes_t hw_ptr,
> + const snd_pcm_uframes_t appl_ptr)
> +{
> + return (io->stream == SND_PCM_STREAM_PLAYBACK) ?
> + snd_pcm_jack_playback_avail(io, hw_ptr, appl_ptr) :
> + snd_pcm_jack_capture_avail(io, hw_ptr, appl_ptr);
> +}
> +
> +static inline snd_pcm_uframes_t snd_pcm_jack_hw_avail(const snd_pcm_ioplug_t* const io,
> + const snd_pcm_uframes_t hw_ptr,
> + const snd_pcm_uframes_t appl_ptr)
> +{
> + /* available data/space which can be transfered by the user application */
> + const snd_pcm_uframes_t user_avail = snd_pcm_jack_avail(io, hw_ptr,
> + appl_ptr);
> +
> + if (user_avail > io->buffer_size) {
> + /* there was an Xrun */
> + return 0;
> + }
> + /* available data/space which can be transfered by the DMA */
> + return io->buffer_size - user_avail;
> +}
Hm, this whole stuff would fit better in ioplug code itself instead of
open-coding in each plugin. Maybe providing a new helper function?
> static int pcm_poll_block_check(snd_pcm_ioplug_t *io)
> {
> static char buf[32];
> @@ -144,7 +206,6 @@ snd_pcm_jack_process_cb(jack_nframes_t nframes, snd_pcm_ioplug_t *io)
> {
> snd_pcm_jack_t *jack = io->private_data;
> snd_pcm_uframes_t hw_ptr;
> - const snd_pcm_channel_area_t *areas;
> snd_pcm_uframes_t xfer = 0;
> unsigned int channel;
>
> @@ -154,40 +215,57 @@ snd_pcm_jack_process_cb(jack_nframes_t nframes, snd_pcm_ioplug_t *io)
> jack->areas[channel].first = 0;
> jack->areas[channel].step = jack->sample_bits;
> }
> -
> - if (io->state != SND_PCM_STATE_RUNNING) {
> - if (io->stream == SND_PCM_STREAM_PLAYBACK) {
> - for (channel = 0; channel < io->channels; channel++)
> - snd_pcm_area_silence(&jack->areas[channel], 0, nframes, io->format);
> - return 0;
> - }
> - }
>
> hw_ptr = jack->hw_ptr;
> - areas = snd_pcm_ioplug_mmap_areas(io);
> -
> - while (xfer < nframes) {
> - snd_pcm_uframes_t frames = nframes - xfer;
> - snd_pcm_uframes_t offset = hw_ptr % io->buffer_size;
> - snd_pcm_uframes_t cont = io->buffer_size - offset;
> -
> - if (cont < frames)
> - frames = cont;
> + if (io->state == SND_PCM_STATE_RUNNING) {
> + const snd_pcm_channel_area_t *areas = snd_pcm_ioplug_mmap_areas(io);
> +
> + while (xfer < nframes) {
> + snd_pcm_uframes_t frames = nframes - xfer;
> + snd_pcm_uframes_t offset = hw_ptr % io->buffer_size;
> + snd_pcm_uframes_t cont = io->buffer_size - offset;
> + snd_pcm_uframes_t hw_avail =
> + snd_pcm_jack_hw_avail(io, hw_ptr,
> + io->appl_ptr);
> +
> + /* stop copying if there is no more data available */
> + if (hw_avail <= 0)
> + break;
> +
> + /* split the snd_pcm_area_copy() function into two parts
> + * if the data to copy passes the buffer wrap around
> + */
> + if (cont < frames)
> + frames = cont;
> +
> + if (hw_avail < frames)
> + frames = hw_avail;
> +
> + for (channel = 0; channel < io->channels; channel++) {
> + if (io->stream == SND_PCM_STREAM_PLAYBACK)
> + snd_pcm_area_copy(&jack->areas[channel], xfer, &areas[channel], offset, frames, io->format);
> + else
> + snd_pcm_area_copy(&areas[channel], offset, &jack->areas[channel], xfer, frames, io->format);
> + }
>
> - for (channel = 0; channel < io->channels; channel++) {
> - if (io->stream == SND_PCM_STREAM_PLAYBACK)
> - snd_pcm_area_copy(&jack->areas[channel], xfer, &areas[channel], offset, frames, io->format);
> - else
> - snd_pcm_area_copy(&areas[channel], offset, &jack->areas[channel], xfer, frames, io->format);
> + hw_ptr += frames;
> + if (hw_ptr >= jack->boundary)
> + hw_ptr -= jack->boundary;
> + xfer += frames;
> }
> -
> - hw_ptr += frames;
> - if (hw_ptr >= jack->boundary)
> - hw_ptr -= jack->boundary;
> - xfer += frames;
> }
> jack->hw_ptr = hw_ptr;
>
> + /* check if requested frames were copied */
> + if (xfer < nframes) {
> + /* always fill the not yet written JACK buffer with silence */
> + if (io->stream == SND_PCM_STREAM_PLAYBACK) {
> + const snd_pcm_uframes_t samples = nframes - xfer;
> + for (channel = 0; channel < io->channels; channel++)
> + snd_pcm_area_silence(&jack->areas[channel], xfer, samples, io->format);
> + }
> + }
> +
Ditto. Basically the whole procedure here is some common pattern, so
it wouldn't be too bad to have it in ioplug side.
thanks,
Takashi
More information about the Alsa-devel
mailing list