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).
-
What could cause these intermittent zero-valued samples and waveform discontinuities in this ADMAIF → ALSA → ADMAIF audio path?
-
Are there any known limitations, synchronization issues, DMA-related issues, or configuration problems on JetPack 6.2.2 that could lead to this behavior?
-
Is there a recommended way to prevent these sample dropouts or zero insertions?
-
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.