Very High Audio Loopback Latency

I have created a code to loop audio between the mic and speaker of a usb headset. I ran code on Axon Board where the latency was more than 1 second whereas when the same code is run on Raspberry Pi 5 I get 0.2second or less delay.
Attaching the code as follows.

/*
command to build
g++ -o pulse_loopback pulse_loopback.cpp -lpulse -lpulse-simple

To run the output
./pulse_loopback

find headset source sink names with

pacmd list-sources
pacmd list-sinks

*/

#include <stdio.h>
#include <unistd.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <fstream>
#include <iostream>

#define BUFSIZE 100

int main(int argc, char *argv[])
{
    // PulseAudio variables
    pa_simple *source_stream = NULL;
    pa_simple *sink_stream = NULL;
    pa_sample_spec source_spec, sink_spec;
    int error;


    const char *source_device = "alsa_input.usb-Plantronics_Plantronics_Blackwire_3220_Series_834A0F341D35A841AEB199CC987380A8-00.analog-stereo";
    const char *sink_device = "alsa_output.usb-Plantronics_Plantronics_Blackwire_3220_Series_834A0F341D35A841AEB199CC987380A8-00.analog-stereo";

    // Create the source stream
    source_spec.format = PA_SAMPLE_FLOAT32LE;
    source_spec.channels = 1;
    source_spec.rate = 8000;

    if (!(source_stream = pa_simple_new(NULL, "PulseAudioExample", PA_STREAM_RECORD, source_device, "Record", &source_spec, NULL, NULL, &error)))
    {
        fprintf(stderr, "pa_simple_new() failed: %s\n", pa_strerror(error));
        goto finish;
    }

    // Create the sink stream
    sink_spec.format = PA_SAMPLE_FLOAT32LE;
    sink_spec.channels = 1;
    sink_spec.rate = 8000;


    if (!(sink_stream = pa_simple_new(NULL, "PulseAudioExample", PA_STREAM_PLAYBACK, sink_device, "Playback", &sink_spec, NULL, NULL, &error)))
    {
        fprintf(stderr, "pa_simple_new() failed: %s\n", pa_strerror(error));
        goto finish;
    }

    // Read from source and write to sink
    uint8_t buf[BUFSIZE];
    while (true)
    {
        // Read from source
        if (pa_simple_read(source_stream, buf, sizeof(buf), &error) < 0)
        {
            fprintf(stderr, __FILE__ ": pa_simple_read() failed: %s\n", pa_strerror(error));
            goto finish;
        }

        // Write to sink
        if (pa_simple_write(sink_stream, buf, sizeof(buf), &error) < 0)
        {
            fprintf(stderr, __FILE__ ": pa_simple_write() failed: %s\n", pa_strerror(error));
            goto finish;
        }
    }

    // Cleanup
finish:
    if (source_stream)
        pa_simple_free(source_stream);
    if (sink_stream)
        pa_simple_free(sink_stream);
    return 0;
}

Hey, so basically you are trying to loopback the audio from axon back to the your windows! Tell me what exactly you are using to record the latency? I will look into it.

If you look into the code shared, it reads audio samples from a attached usb headset mic and returns the read samples back to the same usb headset’s speaker.

No windows involved. It is just reading samples from mic and writing them back on the speaker.

Latency I record on stop watch. I speak 1,2,3 and wait for 1,2,3 to be played back to me. the time between me speaking 3 and start playing back 1 is around 1 second. In raspberry pi you will not get any change to record time because as you stop speaking 3 all the data would have been already played back as there is very minimal delay. but in this board it takes lot of time.

okay, great. I’ll will check!

hey! can you tell if pi 5 is using pipewire or pulseaudio, you may check using pactl info | grep -i 'Server Name'? if it’s pipewire could help me by sending the output of pw-top?

On Raspberry Pi -

Server Name: pulseaudio

what about output of pacmd list-sources | grep 'latency: [1-9]' && pacmd list-sinks | grep 'latency: [1-9]'

and if possible also try using alsaloop -P hw:0,0 -C hw:0,0 -r 96000 -f S16_LE check if you still have latency.

Try using alsa libasound2. Here is the sample code, you won’t get any latency:

/* To build use:
 * g++ -o alsa_loopback alsa_loopback.cpp -lasound
 *
 * Find hw:<card>,<device> number using:
 * arecord -l
 * aplay -l
 *
 * Stop pulseaudio before running: systemctl --user stop pulseaudio pulseaudio.socket
 *
 * You might need to configure BUFFER_SIZE manually in case it overruns or underruns.
 */


#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>

#define BUFFER_SIZE 16
#define SAMPLE_RATE 44100
#define CHANNELS 2
#define FORMAT SND_PCM_FORMAT_S16_LE

void error_exit(const char *message, int err) {
    fprintf(stderr, "%s: %s\n", message, snd_strerror(err));
    exit(EXIT_FAILURE);
}

int main() {
    snd_pcm_t *capture_handle, *playback_handle;
    snd_pcm_hw_params_t *hw_params;
    char *buffer;
    int err;

    buffer = (char*)malloc(BUFFER_SIZE);
    if (!buffer) {
        perror("Failed to allocate buffer");
        return EXIT_FAILURE;
    }

    // Open capture device
    if ((err = snd_pcm_open(&capture_handle, "hw:0,0", SND_PCM_STREAM_CAPTURE, 0)) < 0)
        error_exit("Cannot open capture device", err);

    // Open playback device
    if ((err = snd_pcm_open(&playback_handle, "hw:0,0", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
        error_exit("Cannot open playback device", err);

    // Configure capture device
    snd_pcm_hw_params_malloc(&hw_params);
    snd_pcm_hw_params_any(capture_handle, hw_params);
    snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(capture_handle, hw_params, FORMAT);
    snd_pcm_hw_params_set_rate(capture_handle, hw_params, SAMPLE_RATE, 0);
    snd_pcm_hw_params_set_channels(capture_handle, hw_params, CHANNELS);

    if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0)
        error_exit("Cannot set capture parameters", err);
    snd_pcm_hw_params_free(hw_params);

    // Configure playback device
    snd_pcm_hw_params_malloc(&hw_params);
    snd_pcm_hw_params_any(playback_handle, hw_params);
    snd_pcm_hw_params_set_access(playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(playback_handle, hw_params, FORMAT);
    snd_pcm_hw_params_set_rate(playback_handle, hw_params, SAMPLE_RATE, 0);
    snd_pcm_hw_params_set_channels(playback_handle, hw_params, CHANNELS);

    if ((err = snd_pcm_hw_params(playback_handle, hw_params)) < 0)
        error_exit("Cannot set playback parameters", err);
    snd_pcm_hw_params_free(hw_params);

    printf("Starting audio loopback...\n");

    while (1) {
        // Capture audio
        if ((err = snd_pcm_readi(capture_handle, buffer, BUFFER_SIZE / 4)) < 0) {
            if (err == -EPIPE) {
                fprintf(stderr, "Capture buffer overrun\n");
                snd_pcm_prepare(capture_handle);
            } else {
                error_exit("Capture error", err);
            }
        }

        // Playback audio
        if ((err = snd_pcm_writei(playback_handle, buffer, BUFFER_SIZE / 4)) < 0) {
            if (err == -EPIPE) {
                fprintf(stderr, "Playback buffer underrun\n");
                snd_pcm_prepare(playback_handle);
            } else {
                error_exit("Playback error", err);
            }
        }
    }

    free(buffer);
    snd_pcm_close(capture_handle);
    snd_pcm_close(playback_handle);

    return 0;
}

meanwhile I will look into the matter with pulseaudio!

1 Like

It seems the reason was about setting the latency in pulse audio thru environment variable.

If you will run with this command, we are getting the desired result.

PULSE_LATENCY_MSEC=10 ./pulse_loopback

Let us know if you have further queries.

All of your last 3 solutions have worked.

Appreciate!!!

1 Like