I finally managed to get my hacked up version of LibAPRS for ESP32 transmitting properly. I've had it almost working for several weeks, but couldn't get the audio output to be smooth -- it would occasionally jump suddenly between values. (The AFSK modulation used in AX.25 is supposed to be continuous-phase, and LibAPRS produces correct values, so this was a problem in my code.) I think I was underflowing my I2S buffers or something. By tweaking buffer sizes (both audio and AFSK bytes), it finally seems to be somewhat reliable. Good thing the ESP32 has a lot more memory than an ATMEGA328P.
It's still not 100% reliable, even when going straight from audio output to my KPC-3+. Seems to decode properly about 50% of the time. But I was able to transmit at least one packet to an APRS IGate!