Intermittent waveform discontinuities and zero-valued samples when routing audio through ADMAIF on Jetson Orin Nano

Hello,

I am evaluating audio processing on a Jetson Orin Nano running JetPack 6.2.2.

I created a simple ALSA application that:

  • Captures audio from ADMAIF1 (hw:APE,0)

  • Applies a +6 dB gain (2× multiplication with saturation)

  • Plays the result back through ADMAIF2 (hw:APE,1)

The source signal is a 660 Hz sine wave.

Environment

  • Board: Jetson Orin Nano

  • JetPack: 6.2.2

  • Sample rate: 48 kHz

  • Channels: 2

  • Format: S32_LE

  • External audio interface: MCHStreamer (ASIO device on PC side)

ALSA routing configuration

Before running the application, I execute the following mixer settings:

# ==================================================
# audio format
# ==================================================
amixer -c APE cset name='I2S2 Capture Audio Bit Format'  "32"
amixer -c APE cset name='I2S2 Client Bit Format'         "32"
amixer -c APE cset name='I2S2 Playback Audio Bit Format' "32"

amixer -c APE cset name='I2S2 Sample Rate' 48000

# ==================================================
# I2S
# ==================================================
amixer -c APE cset name='I2S2 Capture Audio Channels' 2
amixer -c APE cset name='I2S2 Client Channels' 2
amixer -c APE cset name='I2S2 Playback Audio Channels' 2

amixer -c APE cset name='I2S2 BCLK Ratio' 0

amixer -c APE cset name='I2S2 FSYNC Width' 32

amixer -c APE cset name='I2S2 codec frame mode' 'i2s'

amixer -c APE cset name='I2S2 codec master mode' 'cbm-cfm'

# ==================================================
# routing
# ==================================================
amixer -c APE cset name='I2S2 Mux' 'None'
amixer -c APE cset name='I2S4 Mux' 'None'
amixer -c APE cset name='ADMAIF1 Mux' 'None'
amixer -c APE cset name='ADMAIF2 Mux' 'None'

amixer -c APE cset name="I2S2 Loopback" Off
amixer -c APE cset name="ADMAIF1 Mux" I2S2
amixer -c APE cset name="I2S2 Mux" ADMAIF2

Application command

./admaif_gain -C hw:APE,0 -P hw:APE,1 -c 2 -r 48000 -D dump.raw

The application uses:

  • Period size: 512 frames

  • Buffer size: 4096 frames

The source code is attached below.

// admaif_gain.c
// ADMAIF1(capture) -> +6 dB -> ADMAIF2(playback)
// 32-bit signed little-endian interleaved audio

#include <alsa/asoundlib.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <unistd.h>

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

static void usage(const char *program)
{
    fprintf(stderr,
            "Usage: %s [options]\n"
            "\n"
            "Options:\n"
            "  -C DEVICE   capture device  (default: hw:APE,0 / ADMAIF1)\n"
            "  -P DEVICE   playback device (default: hw:APE,1 / ADMAIF2)\n"
            "  -c CHANNELS channel count   (default: 2)\n"
            "  -r RATE     sample rate     (default: 48000)\n"
            "  -p FRAMES   period frames   (default: 512)\n"
            "  -b FRAMES   buffer frames   (default: 4096)\n"
            "  -D FILE     dump input signal before +6 dB as raw S32_LE\n"
            "  -h          show this help\n",
            program);
}

static int xrun_recover(snd_pcm_t *pcm, int err)
{
    if (err == -EPIPE) {
        err = snd_pcm_prepare(pcm);
        if (err < 0) return err;
        return 0;
    }
    if (err == -ESTRPIPE) {
        while ((err = snd_pcm_resume(pcm)) == -EAGAIN)
            usleep(1000);
        if (err < 0) {
            err = snd_pcm_prepare(pcm);
            if (err < 0) return err;
        }
        return 0;
    }
    return err;
}

static int open_pcm(snd_pcm_t **handle,
                    const char *name,
                    snd_pcm_stream_t stream,
                    unsigned int rate,
                    unsigned int channels,
                    snd_pcm_uframes_t period_frames,
                    snd_pcm_uframes_t buffer_frames)
{
    snd_pcm_hw_params_t *hw = NULL;
    int err;

    err = snd_pcm_open(handle, name, stream, 0);
    if (err < 0) return err;

    snd_pcm_hw_params_malloc(&hw);
    snd_pcm_hw_params_any(*handle, hw);

    err = snd_pcm_hw_params_set_access(*handle, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err < 0) goto fail;

    err = snd_pcm_hw_params_set_format(*handle, hw, SND_PCM_FORMAT_S32_LE);
    if (err < 0) goto fail;

    err = snd_pcm_hw_params_set_channels(*handle, hw, channels);
    if (err < 0) goto fail;

    unsigned int actual_rate = rate;
    err = snd_pcm_hw_params_set_rate_near(*handle, hw, &actual_rate, 0);
    if (err < 0) goto fail;
    if (actual_rate != rate) {
        fprintf(stderr, "warning: rate adjusted from %u to %u\n", rate, actual_rate);
    }

    snd_pcm_uframes_t p = period_frames;
    err = snd_pcm_hw_params_set_period_size_near(*handle, hw, &p, 0);
    if (err < 0) goto fail;

    snd_pcm_uframes_t b = buffer_frames;
    err = snd_pcm_hw_params_set_buffer_size_near(*handle, hw, &b);
    if (err < 0) goto fail;

    err = snd_pcm_hw_params(*handle, hw);
    if (err < 0) goto fail;

    snd_pcm_hw_params_free(hw);
    return 0;

fail:
    snd_pcm_hw_params_free(hw);
    snd_pcm_close(*handle);
    *handle = NULL;
    return err;
}

static inline int32_t gain_2x_saturate(int32_t x)
{
    int64_t y = (int64_t)x * 2; // +6 dB ≈ 2x
    if (y > INT32_MAX) return INT32_MAX;
    if (y < INT32_MIN) return INT32_MIN;
    return (int32_t)y;
}

int main(int argc, char **argv)
{
    const char *capture_dev = "hw:APE,0";  // ADMAIF1
    const char *playback_dev = "hw:APE,1"; // ADMAIF2
    unsigned int channels = 2;
    unsigned int rate = 48000;
    snd_pcm_uframes_t frames = 512;
    snd_pcm_uframes_t buffer_frames = 4096;
    const char *dump_path = NULL;

    int opt;
    while ((opt = getopt(argc, argv, "C:P:c:r:p:b:D:h")) != -1) {
        switch (opt) {
        case 'C':
            capture_dev = optarg;
            break;
        case 'P':
            playback_dev = optarg;
            break;
        case 'c':
            channels = (unsigned int)atoi(optarg);
            break;
        case 'r':
            rate = (unsigned int)atoi(optarg);
            break;
        case 'p':
            frames = (snd_pcm_uframes_t)atoi(optarg);
            break;
        case 'b':
            buffer_frames = (snd_pcm_uframes_t)atoi(optarg);
            break;
        case 'D':
            dump_path = optarg;
            break;
        case 'h':
            usage(argv[0]);
            return EXIT_SUCCESS;
        default:
            usage(argv[0]);
            return EXIT_FAILURE;
        }
    }

    if (optind < argc || channels == 0 || rate == 0 || frames == 0 || buffer_frames == 0 ||
        buffer_frames < frames) {
        usage(argv[0]);
        return EXIT_FAILURE;
    }

    snd_pcm_t *cap = NULL, *pb = NULL;
    FILE *dump_file = NULL;
    int err;

    if (dump_path) {
        dump_file = fopen(dump_path, "wb");
        if (!dump_file) {
            perror("open dump file");
            return EXIT_FAILURE;
        }
    }

    err = open_pcm(&cap, capture_dev, SND_PCM_STREAM_CAPTURE, rate, channels, frames,
                   buffer_frames);
    if (err < 0) {
        if (dump_file) fclose(dump_file);
        die("open capture", err);
    }

    err = open_pcm(&pb, playback_dev, SND_PCM_STREAM_PLAYBACK, rate, channels, frames,
                   buffer_frames);
    if (err < 0) {
        if (dump_file) fclose(dump_file);
        die("open playback", err);
    }

    size_t samples_per_period = (size_t)frames * channels;
    int32_t *buf = calloc(samples_per_period, sizeof(int32_t));
    if (!buf) {
        fprintf(stderr, "calloc failed\n");
        snd_pcm_close(cap);
        snd_pcm_close(pb);
        if (dump_file) fclose(dump_file);
        return EXIT_FAILURE;
    }

    err = snd_pcm_prepare(cap);
    if (err < 0) die("prepare capture", err);
    err = snd_pcm_prepare(pb);
    if (err < 0) die("prepare playback", err);

        fprintf(stderr, "Running: %s -> %s, %u ch, %u Hz, %lu frames/period, %lu frames/buffer\n",
            capture_dev, playback_dev, channels, rate, (unsigned long)frames,
            (unsigned long)buffer_frames);
    if (dump_file) {
        fprintf(stderr, "Dump before gain: %s\n", dump_path);
    }

    while (1) {
        snd_pcm_sframes_t nread = snd_pcm_readi(cap, buf, frames);
        if (nread < 0) {
            err = xrun_recover(cap, (int)nread);
            if (err < 0) die("capture xrun", err);
            continue;
        }

        size_t total_samples = (size_t)nread * channels;
        if (dump_file && fwrite(buf, sizeof(int32_t), total_samples, dump_file) != total_samples) {
            perror("write dump file");
            break;
        }

        for (size_t i = 0; i < total_samples; ++i) {
            buf[i] = gain_2x_saturate(buf[i]);
        }

        size_t written_frames = 0;
        while (written_frames < (size_t)nread) {
            snd_pcm_sframes_t nw = snd_pcm_writei(pb,
                                                  buf + written_frames * channels,
                                                  (snd_pcm_uframes_t)(nread - written_frames));
            if (nw < 0) {
                err = xrun_recover(pb, (int)nw);
                if (err < 0) die("playback xrun", err);
                continue;
            }
            written_frames += (size_t)nw;
        }
    }

    free(buf);
    if (dump_file) fclose(dump_file);
    snd_pcm_close(cap);
    snd_pcm_close(pb);
    return 0;
}

Expected result

The output should be a continuous 660 Hz sine wave with approximately +6 dB gain.

Actual result

The recorded output occasionally contains:

  • Waveform discontinuities

  • Short segments of zero-valued samples

The zero-valued sections are typically:

  • About 10–80 samples long

  • Observed approximately 2–3 times during a 30-second recording

The issue is audible and also visible in the recorded waveform.

Questions

I would like to understand why this behavior occurs.

The application simply reads audio from ADMAIF1, applies a 2× gain operation, and writes the result to ADMAIF2. However, the output occasionally contains waveform discontinuities and short runs of zero-valued samples (10–80 samples).

  1. What could cause these intermittent zero-valued samples and waveform discontinuities in this ADMAIF → ALSA → ADMAIF audio path?

  2. Are there any known limitations, synchronization issues, DMA-related issues, or configuration problems on JetPack 6.2.2 that could lead to this behavior?

  3. Is there a recommended way to prevent these sample dropouts or zero insertions?

  4. Are there specific debug methods or kernel/ALSA logs that can help identify where the samples are being lost (I2S input, ADMAIF, DMA engine, ALSA PCM layer, or playback path)?

Since the dropouts are very short (10–80 samples) and occur only a few times during a 30-second recording, I suspect that samples may be intermittently lost somewhere in the audio pipeline. I would appreciate any advice on how to identify the exact stage where this occurs and how to avoid it.

Any guidance on the root cause and possible countermeasures would be greatly appreciated.

Thank you.

Hi,
Thanks for sharing the details.
could you please check the same behavior using arecord and aplay directly, without your application in the middle?
Also, please share the pinmux settings for I2S2, even if they appear correct, so we can verify the configuration.
Is I2S2 connected to an external codec, or is DIN connected to DOUT on the Tegra I2S2 itself?

Thanks for your reply.

  • We checked the behavior using arecord and aplay directly, without our application in the middle. In this case, the issue could not be reproduced. The phenomenon only occurs when our application is used.
  • The current I2S2 pinmux configuration is shown below:

=================== Jetson Expansion Header Tool ===================
|                                                                    |
|                      3.3V (  1) .. (  2) 5V                        |
|                      i2c8 (  3) .. (  4) 5V                        |
|                      i2c8 (  5) .. (  6) GND                       |
|                    unused (  7) .. (  8) uarta                     |
|                       GND (  9) .. ( 10) uarta                     |
|                    unused ( 11) .. ( 12) i2s2_sclk                 |
|                    unused ( 13) .. ( 14) GND                       |
|                    unused ( 15) .. ( 16) unused                    |
|                      3.3V ( 17) .. ( 18) unused                    |
|                    unused ( 19) .. ( 20) GND                       |
|                    unused ( 21) .. ( 22) unused                    |
|                    unused ( 23) .. ( 24) unused                    |
|                       GND ( 25) .. ( 26) unused                    |
|                      i2c2 ( 27) .. ( 28) i2c2                      |
|                    unused ( 29) .. ( 30) GND                       |
|                    unused ( 31) .. ( 32) unused                    |
|                    unused ( 33) .. ( 34) GND                       |
|                   i2s2_fs ( 35) .. ( 36) unused                    |
|                    unused ( 37) .. ( 38) i2s2_din                  |
|                       GND ( 39) .. ( 40) i2s2_dout                 |
|                                                                    |
|====================================================================|
  • I2S2 is connected to MCHStreamer. We also tested with MCHStreamer directly connected, and in that configuration the issue did not occur.

Please let us know if you need any additional information or further tests.

Best regards,

— 🤖 This is an automated AI response. I’m here to help, but please verify important details! —

The issue you’re experiencing with intermittent waveform discontinuities and zero-valued samples in the ADMAIF → ALSA → ADMAIF audio path on the Jetson Orin Nano could be related to several factors, including synchronization issues, DMA-related problems, or configuration issues. However, without specific details on the system’s configuration, ALSA settings, and the exact nature of the discontinuities, it’s challenging to pinpoint the exact cause.

Given the information provided, here are some potential steps and considerations to help diagnose and possibly resolve the issue:

  1. ALSA Configuration: Ensure that the ALSA configuration is correctly set up for your application. This includes properly configuring the period size, buffer size, and ensuring that the sample rate and format (S32_LE in your case) are correctly set for both capture and playback devices.

  2. DMA and Synchronization: The issue might be related to DMA (Direct Memory Access) transfers or synchronization between the capture and playback paths. Checking the DMA settings and ensuring proper synchronization between the ADMAIF1 capture and ADMAIF2 playback might help resolve the issue.

  3. Interrupt Handling and Priorities: Sometimes, interrupt handling priorities can affect real-time audio processing. Ensuring that the interrupts related to audio processing have appropriate priorities might help in reducing or eliminating the discontinuities.

  4. System Load and Resource Utilization: High system load or contention for resources (like CPU, memory, or bus bandwidth) could cause intermittent dropouts. Monitoring system resources and adjusting the system load or prioritizing the audio processing task might help.

  5. Kernel and ALSA Versions: The behavior could be version-specific. Checking if there are any known issues in the JetPack 6.2.2 version you’re using and considering updates or patches might be beneficial.

  6. Debugging: Utilizing debugging tools and logs (such as ALSA’s debug logging capabilities) can provide insights into where in the pipeline the samples are being lost. This could involve enabling verbose logging for ALSA, checking system logs for errors related to audio or DMA, and using tools like alsamixer or amixer to adjust settings and observe the behavior.

  7. Hardware Considerations: Although less likely given the software nature of the issue, hardware-specific configurations or limitations (e.g., related to the MCHStreamer external audio interface) could play a role. Ensuring that all hardware components are properly configured and compatible with the Jetson Orin Nano is essential.

To further assist, more specific details about the system configuration, the exact nature of the discontinuities (e.g., are they perfectly silent, or is there noise?), and any attempts made so far to debug the issue would be helpful. Additionally, checking the NVIDIA forums, ALSA documentation, and Jetson Orin Nano-specific resources for similar issues or known limitations might provide valuable insights or solutions.

Given the constraints of the provided context, it’s recommended to explore the Jetson Orin Nano documentation, ALSA configuration guides, and NVIDIA’s developer forums for more detailed troubleshooting steps and potential solutions tailored to your specific setup and version of JetPack.

— We support team will also look into your issue to have further suggestions soon. please stay tuned. —

Hi,

Thank you for the update.

Based on your findings — arecord/aplay working correctly with no issues observed — we can confirm there are no issues on the hardware or audio drivers side. The intermittent zero-valued samples and waveform discontinuities are specific to your application’s capture-process-playback loop.

This is typically caused by buffer underruns (xruns) when the processing loop doesn’t complete within the period window. Please refer to the following for guidance on handling this in your application:

Thanks