#include "audio_out.h" #include #include "esp_ae_rate_cvt.h" #include "esp_heap_caps.h" #include "esp_log.h" #define OUT_RATE 48000 #define CONV_FRAMES 4096 // per rate converter call static const char *TAG = "audio_out"; struct audio_conv { player_src_t src; const char *name; esp_ae_rate_cvt_handle_t cvt; // NULL: no conversion (source is 48 kHz) uint32_t rate; // input rate the converter was opened for int32_t *in32, *out32; // stereo int32 work buffers (PSRAM) }; audio_conv_t *audio_conv_create(player_src_t src, const char *name) { audio_conv_t *c = calloc(1, sizeof(*c)); if (!c) { return NULL; } c->src = src; c->name = name; c->in32 = heap_caps_malloc(CONV_FRAMES * 2 * sizeof(int32_t), MALLOC_CAP_SPIRAM); c->out32 = heap_caps_malloc(CONV_FRAMES * 2 * 2 * sizeof(int32_t), MALLOC_CAP_SPIRAM); if (!c->in32 || !c->out32) { free(c->in32); free(c->out32); free(c); return NULL; } return c; } static void set_rate(audio_conv_t *c, uint32_t rate) { if (c->cvt) { esp_ae_rate_cvt_close(c->cvt); c->cvt = NULL; } c->rate = rate; if (rate == OUT_RATE) { return; } esp_ae_rate_cvt_cfg_t cfg = { .src_rate = rate, .dest_rate = OUT_RATE, .channel = 2, .bits_per_sample = 32, .complexity = 3, .perf_type = ESP_AE_RATE_CVT_PERF_TYPE_SPEED, }; if (esp_ae_rate_cvt_open(&cfg, &c->cvt) != ESP_AE_ERR_OK) { ESP_LOGE(TAG, "%s: rate converter %lu -> %d Hz: open failed", c->name, (unsigned long)rate, OUT_RATE); } else { ESP_LOGI(TAG, "%s: rate converter %lu -> %d Hz", c->name, (unsigned long)rate, OUT_RATE); } } size_t audio_conv_write(audio_conv_t *c, const int16_t *pcm, uint32_t frames, int ch, uint32_t rate) { if (rate != c->rate) { set_rate(c, rate); } size_t produced = 0; while (frames) { uint32_t n = frames < CONV_FRAMES ? frames : CONV_FRAMES; for (uint32_t i = 0; i < n; i++) { // to stereo int32 (full scale = INT32_MAX) int32_t l = (int32_t)pcm[i * ch] << 16; c->in32[i * 2] = l; c->in32[i * 2 + 1] = ch > 1 ? (int32_t)pcm[i * ch + 1] << 16 : l; } const int32_t *out = c->in32; uint32_t out_n = n; if (c->cvt) { out_n = CONV_FRAMES * 2; if (esp_ae_rate_cvt_process(c->cvt, c->in32, n, c->out32, &out_n) != ESP_AE_ERR_OK) { ESP_LOGW(TAG, "%s: rate conversion failed", c->name); return produced; } out = c->out32; } produced += player_write(c->src, out, out_n); pcm += n * ch; frames -= n; } return produced; }