Verify Frame Size & Alignment: Ensure audio buffers fed to OPUS strictly match valid frame durations (10ms, 20ms, 40ms, or 60ms @ 16 kHz = 320, 640, 1280 samples). Never pass partial or unaligned I2S buffers to the encoder/decoder.
Check Compiler Defines: Make sure -DFIXED_POINT=1 and -DOPUS_BUILD are set in build flags (build_flags in PlatformIO or cflags in Arduino IDE) to prevent Q10 fixed-point overflows.
Isolate Cores: Pin the Edge Impulse inferencing task to Core 1 and keep I2S audio capture / OPUS streaming on Core 0 (or vice versa) with a thread-safe FreeRTOS Queue to prevent frame corruption during wake-word detection.
Diagnosis
It’s a libopus internal library/configuration bug combined with buffer corruption / invalid payload state.
Here is what the crash log reveals:
1. What the Assertion Error Means
The crash happens deep inside the Opus encoder/decoder’s SILK narrowband codec loop: Fatal (internal) error in lib/arduino-libopus/src/opus-1.3.1/silk/NSQ_del_dec.c, line 584: assertion failed: psSampleState[ k ][ 0 ].RD_Q10 >= 0
NSQ_del_dec.c is the Noise Shaping Quantization (Delayed Decision) module in the SILK layer.
RD_Q10 tracks Rate-Distortion cost during audio quantization.
Under normal conditions, rate-distortion cost is strictly positive. The assertion triggers because RD_Q10 wrapped around or went negative (< 0) due to integer overflow / fixed-point scaling failure or corrupted input audio frames being fed to the encoder.
2. The Core Causes
A. Feeding Invalid/Corrupted Audio Packets to opus_decode()
The user triggers an Edge Impulse wake-word inference loop, then immediately passes audio streams through OPUS.
If I2S buffer reads overlap with Edge Impulse running inference on CPU Core 0, missing bytes or misaligned frame sizes (e.g., passing 10ms of audio when the decoder expects 20ms frame sizes) cause SILK’s internal state machine to calculate invalid floating/fixed-point metrics, hitting the assert().
B. Floating-Point vs. Fixed-Point Configuration Conflict
arduino-libopus 1.3.1 can be compiled in FIXED_POINT (integer) or FLOATING_POINT mode.
On the ESP32-S3 (Xtensa LX7 with FPU), compiling libopus without -DFIXED_POINT or using mismatching Q-format definitions in config.h causes internal SILK state variables (RD_Q10) to overflow.
C. Core 0 Stack/Memory Contention with Edge Impulse
The crash shows abort() was called at PC 0x420274c3 on core 0.
Edge Impulse models on the XIAO ESP32-S3 Sense often run inference on Core 0 or tie up PSRAM. If I2S audio capture drops frames or overwrites the OPUS input stream during model evaluation, the decoder gets bad frame headers.