aes67_audio.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990
  1. /*
  2. * SPDX-FileCopyrightText: 2026 DatanoiseTV
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. /*
  7. * I2S audio driver for AES67 on ESP32-P4.
  8. *
  9. * Uses ESP-IDF I2S standard mode with DMA for audio capture and playback.
  10. * Ring buffers bridge between I2S DMA transfers and the RTP engine.
  11. * DMA buffers reside in internal SRAM; ring buffers may use PSRAM.
  12. */
  13. #include "aes67_audio.h"
  14. #include <string.h>
  15. #include "esp_log.h"
  16. #include "esp_heap_caps.h"
  17. #include "freertos/FreeRTOS.h"
  18. #include "freertos/task.h"
  19. #include "driver/i2s_std.h"
  20. #include "driver/i2s_common.h"
  21. #include "esp_timer.h"
  22. #include "esp_cache.h"
  23. #include "hal/i2s_ll.h"
  24. #include "hal/hal_utils.h"
  25. #include "soc/hp_sys_clkrst_struct.h"
  26. static const char *TAG = "aes67_audio";
  27. /* Kept for future capture (RX) task */
  28. /* I2S DMA: 2 descriptors at 16 frames (0.33ms) each.
  29. * ISR fires at 3000Hz. DMA ring latency = 0.33ms.
  30. * Stable at all ptimes (0.125ms to 4ms) with 100% utilization.
  31. * DMA_FRAME_NUM=6 (8000Hz ISR) causes 0.03% loss at the edge. */
  32. #define DMA_DESC_NUM 2
  33. #define DMA_FRAME_NUM 16
  34. struct aes67_audio_ctx {
  35. i2s_chan_handle_t tx_chan;
  36. i2s_chan_handle_t rx_chan;
  37. aes67_audio_config_t config;
  38. /* Ring buffers (PSRAM when use_psram is set) */
  39. int32_t *capture_buf;
  40. int32_t *playback_buf;
  41. uint32_t buf_size_frames;
  42. volatile uint32_t capture_wr;
  43. volatile uint32_t capture_rd;
  44. volatile uint32_t playback_wr;
  45. volatile uint32_t playback_rd;
  46. /* DMA staging buffers (always internal SRAM) */
  47. uint8_t *dma_rx_buf;
  48. uint8_t *dma_tx_buf;
  49. uint32_t dma_buf_size_bytes;
  50. /* Pre-allocated int16 conversion buffer for direct_write.
  51. * Avoids heap_caps_malloc/free on every write call. */
  52. int16_t *i2s_conv_buf;
  53. uint32_t i2s_conv_buf_samples;
  54. /* DMA completion semaphore - signaled by on_sent ISR callback */
  55. SemaphoreHandle_t dma_sem;
  56. /* Staging ring buffer: RTP RX writes int16 data here,
  57. * DMA ISR reads from here to fill DMA descriptors directly. */
  58. int16_t *staging_buf;
  59. uint32_t staging_size; /* Total size in int16 samples */
  60. volatile uint32_t staging_wr;
  61. volatile uint32_t staging_rd;
  62. volatile uint32_t staging_underruns; /* ISR couldn't fill full descriptor */
  63. volatile uint32_t staging_isr_count; /* Total ISR calls */
  64. volatile bool staging_ready; /* ISR reads only when true */
  65. /* Frame callback */
  66. aes67_audio_frame_cb_t frame_cb;
  67. void *frame_cb_user_data;
  68. /* I/O task */
  69. TaskHandle_t io_task;
  70. bool running;
  71. bool use_psram;
  72. /* Samples per frame period: sample_rate * packet_time_us / 1000000 */
  73. uint32_t frame_size;
  74. };
  75. /* --- Helpers --------------------------------------------------------------- */
  76. /* Convert word_length (bytes per sample) to I2S bit width enum.
  77. * Currently unused since we force 16-bit I2S for ES8311 compat,
  78. * but kept for future use with other codecs. */
  79. __attribute__((unused))
  80. static i2s_data_bit_width_t word_length_to_bits(uint8_t word_length)
  81. {
  82. switch (word_length) {
  83. case 2: return I2S_DATA_BIT_WIDTH_16BIT;
  84. case 3: return I2S_DATA_BIT_WIDTH_24BIT;
  85. case 4: return I2S_DATA_BIT_WIDTH_32BIT;
  86. default: return I2S_DATA_BIT_WIDTH_24BIT;
  87. }
  88. }
  89. /* In ESP32-P4 Philips I2S mode with 32-bit slot width, the DMA data
  90. * is already left-justified (MSB-first) in the 32-bit word. This means
  91. * the I2S DMA format matches our internal left-justified int32 format
  92. * directly. No bit shifting is needed regardless of the configured
  93. * data bit width (16/24/32), because the I2S hardware handles the
  94. * slot-to-wire packing. */
  95. /* Available frames in a ring buffer (single-producer / single-consumer safe) */
  96. static inline uint32_t ring_available(uint32_t wr, uint32_t rd, uint32_t capacity)
  97. {
  98. return (wr - rd) % capacity;
  99. }
  100. /* Free frames in a ring buffer */
  101. static inline uint32_t ring_free(uint32_t wr, uint32_t rd, uint32_t capacity)
  102. {
  103. /* Reserve one frame to distinguish full from empty */
  104. return capacity - 1 - ring_available(wr, rd, capacity);
  105. }
  106. /* --- DMA <-> Ring Buffer Conversion --------------------------------------- */
  107. /* Copy DMA buffer (32-bit slots) directly to capture ring buffer.
  108. * Both DMA and ring buffer use left-justified int32 format. */
  109. __attribute__((unused))
  110. static void dma_to_capture_ring(struct aes67_audio_ctx *ctx,
  111. const uint8_t *dma_buf,
  112. uint32_t frame_count)
  113. {
  114. const uint8_t ch = ctx->config.channels;
  115. const uint32_t cap = ctx->buf_size_frames;
  116. const int32_t *src = (const int32_t *)dma_buf;
  117. uint32_t wr = ctx->capture_wr;
  118. for (uint32_t f = 0; f < frame_count; f++) {
  119. uint32_t ring_idx = (wr % cap) * ch;
  120. for (uint8_t c = 0; c < ch; c++) {
  121. ctx->capture_buf[ring_idx + c] = src[f * ch + c];
  122. }
  123. wr++;
  124. }
  125. ctx->capture_wr = wr;
  126. }
  127. /* Read from playback ring buffer and copy to DMA TX buffer.
  128. * Both use left-justified int32 format - direct copy, no shifting. */
  129. __attribute__((unused))
  130. static void playback_ring_to_dma(struct aes67_audio_ctx *ctx,
  131. uint8_t *dma_buf,
  132. uint32_t frame_count)
  133. {
  134. const uint8_t ch = ctx->config.channels;
  135. const uint32_t cap = ctx->buf_size_frames;
  136. int32_t *dst = (int32_t *)dma_buf;
  137. uint32_t rd = ctx->playback_rd;
  138. uint32_t avail = ring_available(ctx->playback_wr, rd, cap);
  139. for (uint32_t f = 0; f < frame_count; f++) {
  140. if (f < avail) {
  141. uint32_t ring_idx = (rd % cap) * ch;
  142. for (uint8_t c = 0; c < ch; c++) {
  143. dst[f * ch + c] = ctx->playback_buf[ring_idx + c];
  144. }
  145. rd++;
  146. } else {
  147. for (uint8_t c = 0; c < ch; c++) {
  148. dst[f * ch + c] = 0;
  149. }
  150. }
  151. }
  152. ctx->playback_rd = rd;
  153. }
  154. /* --- DMA callbacks -------------------------------------------------------- */
  155. /* --- StreamBuffer-based ISR playback --- */
  156. /* The Ethernet hook writes variable-size packets (52-192 frames depending
  157. * on source ptime). The DMA ISR reads exactly 1536 bytes per fire
  158. * (one descriptor). A byte-stream buffer naturally handles variable
  159. * packet sizes by concatenating them until the ISR has a full descriptor. */
  160. static StreamBufferHandle_t s_isr_stream_buf = NULL;
  161. /* ISR diagnostics */
  162. static volatile uint32_t s_isr_fires = 0;
  163. static volatile uint32_t s_isr_full = 0;
  164. static volatile uint32_t s_isr_empty = 0;
  165. /* Write audio data to the stream buffer (called from Ethernet hook).
  166. * Handles any packet size -- multiple small packets accumulate until
  167. * the ISR reads a full DMA descriptor. */
  168. void aes67_audio_slot_write(const void *data, uint32_t len)
  169. {
  170. if (s_isr_stream_buf && len > 0) {
  171. xStreamBufferSend(s_isr_stream_buf, data, len, 0);
  172. }
  173. }
  174. /* Last frame for sample-hold on underrun (less audible than silence) */
  175. static int32_t s_last_frame[2] = {0, 0}; /* Stereo max */
  176. /* ISR callback: reads from the stream buffer to fill one DMA descriptor.
  177. * On underrun, uses sample-hold (repeat last frame) instead of silence
  178. * to mask the ~8% EMAC packet loss with minimal artifacts. */
  179. static IRAM_ATTR bool on_i2s_tx_sent_sbuf(i2s_chan_handle_t handle,
  180. i2s_event_data_t *event,
  181. void *user_ctx)
  182. {
  183. BaseType_t woken = pdFALSE;
  184. s_isr_fires++;
  185. if (s_isr_stream_buf) {
  186. size_t received = xStreamBufferReceiveFromISR(s_isr_stream_buf,
  187. event->dma_buf,
  188. event->size,
  189. &woken);
  190. if (received >= event->size) {
  191. s_isr_full++;
  192. /* Save last frame for sample-hold on next underrun */
  193. int32_t *samples = (int32_t *)event->dma_buf;
  194. uint32_t last_idx = (event->size / sizeof(int32_t)) - 2;
  195. s_last_frame[0] = samples[last_idx];
  196. s_last_frame[1] = samples[last_idx + 1];
  197. } else {
  198. s_isr_empty++;
  199. /* Sample-hold: fill with last known frame instead of silence.
  200. * Decays toward zero to avoid DC offset on stream stop. */
  201. int32_t *dst = (int32_t *)event->dma_buf;
  202. uint32_t n_samples = event->size / sizeof(int32_t);
  203. /* Fill any partial data gap */
  204. uint32_t start = received / sizeof(int32_t);
  205. for (uint32_t i = start; i < n_samples; i += 2) {
  206. dst[i] = s_last_frame[0];
  207. dst[i + 1] = s_last_frame[1];
  208. }
  209. /* Decay toward zero (fade over ~10 underruns = 10ms) */
  210. s_last_frame[0] = (s_last_frame[0] * 7) / 8;
  211. s_last_frame[1] = (s_last_frame[1] * 7) / 8;
  212. }
  213. } else {
  214. s_isr_empty++;
  215. memset(event->dma_buf, 0, event->size);
  216. }
  217. esp_cache_msync(event->dma_buf, event->size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
  218. return woken == pdTRUE;
  219. }
  220. /* Log ISR stats -- only on first lock and on underruns */
  221. void aes67_audio_log_isr_stats(void)
  222. {
  223. static bool first_100_logged = false;
  224. static uint32_t prev_empty_total = 0;
  225. uint32_t fires = s_isr_fires;
  226. uint32_t full = s_isr_full;
  227. uint32_t empty = s_isr_empty;
  228. s_isr_fires = 0;
  229. s_isr_full = 0;
  230. s_isr_empty = 0;
  231. if (fires == 0) return;
  232. uint32_t pct = full * 100 / fires;
  233. /* Log once when first reaching 100% */
  234. if (!first_100_logged && pct == 100) {
  235. first_100_logged = true;
  236. ESP_LOGI("dma_isr", "locked: %lu Hz, 100%% utilization", (unsigned long)(fires / 2));
  237. }
  238. /* Log whenever there are underruns (after initial lock) */
  239. if (first_100_logged && empty > 0) {
  240. prev_empty_total += empty;
  241. ESP_LOGW("dma_isr", "underrun: %lu empty in %lu fires (%lu%%), total=%lu",
  242. (unsigned long)empty, (unsigned long)fires,
  243. (unsigned long)pct, (unsigned long)prev_empty_total);
  244. }
  245. }
  246. /* Initialize stream buffer for ISR playback */
  247. void aes67_audio_set_isr_stream_buf(aes67_audio_handle_t handle,
  248. StreamBufferHandle_t sbuf)
  249. {
  250. (void)handle;
  251. s_isr_stream_buf = xStreamBufferCreate(65536, 1);
  252. ESP_LOGI("aes67_audio", "ISR playback stream buffer: 64KB");
  253. }
  254. /* --- I2S RX Capture ISR (I2S -> AES67 TX source) --- */
  255. /* Capture stream buffer: RX DMA ISR writes here, TIC task reads */
  256. static StreamBufferHandle_t s_capture_stream_buf = NULL;
  257. /* RX DMA ISR: fires when a DMA descriptor finishes capturing.
  258. * Copies captured int32 samples to the capture stream buffer.
  259. * The TIC task reads from this buffer to feed RTP source streams. */
  260. static IRAM_ATTR bool on_i2s_rx_recv(i2s_chan_handle_t handle,
  261. i2s_event_data_t *event,
  262. void *user_ctx)
  263. {
  264. if (s_capture_stream_buf && event->size > 0) {
  265. /* Invalidate CPU cache: DMA wrote to memory, CPU cache may be stale */
  266. esp_cache_msync(event->dma_buf, event->size, ESP_CACHE_MSYNC_FLAG_DIR_M2C);
  267. BaseType_t woken = pdFALSE;
  268. xStreamBufferSendFromISR(s_capture_stream_buf, event->dma_buf,
  269. event->size, &woken);
  270. return woken == pdTRUE;
  271. }
  272. return false;
  273. }
  274. /* Get the capture stream buffer (called by TIC task to read captured audio) */
  275. StreamBufferHandle_t aes67_audio_get_capture_buf(aes67_audio_handle_t handle)
  276. {
  277. (void)handle;
  278. return s_capture_stream_buf;
  279. }
  280. /* Initialize capture stream buffer for I2S RX -> RTP TX path */
  281. void aes67_audio_init_capture(aes67_audio_handle_t handle)
  282. {
  283. (void)handle;
  284. s_capture_stream_buf = xStreamBufferCreate(65536, 1);
  285. ESP_LOGI("aes67_audio", "ISR capture stream buffer: 64KB");
  286. }
  287. /* Legacy staging ISR (unused, kept for reference) */
  288. __attribute__((unused))
  289. static IRAM_ATTR bool on_i2s_tx_sent(i2s_chan_handle_t handle,
  290. i2s_event_data_t *event,
  291. void *user_ctx)
  292. {
  293. struct aes67_audio_ctx *ctx = (struct aes67_audio_ctx *)user_ctx;
  294. int16_t *dma_buf = (int16_t *)event->dma_buf;
  295. uint32_t dma_samples = event->size / sizeof(int16_t);
  296. /* Don't read from staging until it has enough data */
  297. if (!ctx->staging_ready) {
  298. memset(dma_buf, 0, event->size);
  299. esp_cache_msync(event->dma_buf, event->size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
  300. ctx->staging_isr_count++;
  301. return false;
  302. }
  303. /* Copy from staging ring to DMA buffer */
  304. uint32_t rd = ctx->staging_rd;
  305. uint32_t wr = ctx->staging_wr;
  306. uint32_t cap = ctx->staging_size;
  307. uint32_t avail = (wr >= rd) ? (wr - rd) : (cap - rd + wr);
  308. uint32_t to_copy = (avail < dma_samples) ? avail : dma_samples;
  309. uint32_t first = cap - (rd % cap);
  310. if (first > to_copy) first = to_copy;
  311. /* Fast copy from staging ring (internal SRAM, no cache issues) */
  312. memcpy(dma_buf, &ctx->staging_buf[rd % cap], first * sizeof(int16_t));
  313. if (to_copy > first) {
  314. memcpy(dma_buf + first, ctx->staging_buf,
  315. (to_copy - first) * sizeof(int16_t));
  316. }
  317. /* Zero-fill if not enough data (silence on underrun) */
  318. if (to_copy < dma_samples) {
  319. memset(dma_buf + to_copy, 0, (dma_samples - to_copy) * sizeof(int16_t));
  320. }
  321. ctx->staging_rd = (rd + to_copy) % cap;
  322. ctx->staging_isr_count++;
  323. if (to_copy < dma_samples) {
  324. ctx->staging_underruns++;
  325. }
  326. /* Flush CPU cache to memory so DMA controller sees the new data.
  327. * ESP32-P4 routes internal SRAM through L1 cache -- without this
  328. * sync, DMA reads stale data and produces crackling. */
  329. esp_cache_msync(event->dma_buf, event->size, ESP_CACHE_MSYNC_FLAG_DIR_C2M);
  330. /* Notify playback task to refill staging buffer */
  331. BaseType_t woken = pdFALSE;
  332. xSemaphoreGiveFromISR(ctx->dma_sem, &woken);
  333. return woken == pdTRUE;
  334. }
  335. /* --- Public API ----------------------------------------------------------- */
  336. esp_err_t aes67_audio_init(const aes67_audio_config_t *audio_config,
  337. const aes67_i2s_pins_t *pins,
  338. bool use_psram,
  339. uint16_t ring_buf_ms,
  340. aes67_audio_handle_t *handle)
  341. {
  342. if (!audio_config || !pins || !handle) {
  343. return ESP_ERR_INVALID_ARG;
  344. }
  345. struct aes67_audio_ctx *ctx = calloc(1, sizeof(*ctx));
  346. if (!ctx) {
  347. ESP_LOGE(TAG, "Failed to allocate audio context");
  348. return ESP_ERR_NO_MEM;
  349. }
  350. memcpy(&ctx->config, audio_config, sizeof(aes67_audio_config_t));
  351. ctx->use_psram = use_psram;
  352. /* Compute frame size: samples per packet period */
  353. ctx->frame_size = (uint32_t)audio_config->sample_rate *
  354. audio_config->packet_time_us / 1000000;
  355. if (ctx->frame_size == 0) {
  356. ESP_LOGE(TAG, "Invalid sample_rate/packet_time combination");
  357. free(ctx);
  358. return ESP_ERR_INVALID_ARG;
  359. }
  360. ESP_LOGI(TAG, "Init: %lu Hz, %u ch, %u bytes/sample, frame_size=%lu",
  361. (unsigned long)audio_config->sample_rate,
  362. audio_config->channels,
  363. audio_config->word_length,
  364. (unsigned long)ctx->frame_size);
  365. /* --- Allocate ring buffers --- */
  366. uint32_t ring_frames = (audio_config->sample_rate * ring_buf_ms) / 1000;
  367. if (ring_frames < ctx->frame_size * 4) {
  368. ring_frames = ctx->frame_size * 4;
  369. }
  370. /* Add 1 for the sentinel frame (full/empty disambiguation) */
  371. ring_frames += 1;
  372. ctx->buf_size_frames = ring_frames;
  373. size_t ring_bytes = ring_frames * audio_config->channels * sizeof(int32_t);
  374. uint32_t alloc_caps = use_psram ? MALLOC_CAP_SPIRAM : MALLOC_CAP_DEFAULT;
  375. ctx->capture_buf = heap_caps_calloc(1, ring_bytes, alloc_caps);
  376. ctx->playback_buf = heap_caps_calloc(1, ring_bytes, alloc_caps);
  377. if (!ctx->capture_buf || !ctx->playback_buf) {
  378. ESP_LOGE(TAG, "Failed to allocate ring buffers (%u bytes each)", (unsigned)ring_bytes);
  379. goto err_free_bufs;
  380. }
  381. /* --- Allocate DMA staging buffers (internal SRAM) --- */
  382. ctx->dma_buf_size_bytes = ctx->frame_size * audio_config->channels * sizeof(int32_t);
  383. ctx->dma_rx_buf = heap_caps_calloc(1, ctx->dma_buf_size_bytes,
  384. MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
  385. ctx->dma_tx_buf = heap_caps_calloc(1, ctx->dma_buf_size_bytes,
  386. MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
  387. if (!ctx->dma_rx_buf || !ctx->dma_tx_buf) {
  388. ESP_LOGE(TAG, "Failed to allocate DMA buffers (%lu bytes each)",
  389. (unsigned long)ctx->dma_buf_size_bytes);
  390. goto err_free_bufs;
  391. }
  392. /* --- Configure I2S channels --- */
  393. /* Official ESP-IDF es8311 example for ESP32-P4 uses I2S_NUM_0 */
  394. i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
  395. chan_cfg.dma_desc_num = DMA_DESC_NUM;
  396. chan_cfg.dma_frame_num = DMA_FRAME_NUM;
  397. /* When using ISR callback, auto_clear doesn't matter because the ISR
  398. * fills every descriptor (with data or silence). Set false for
  399. * i2s_channel_write compatibility in non-ISR modes. */
  400. chan_cfg.auto_clear = (s_isr_stream_buf != NULL);
  401. esp_err_t ret = i2s_new_channel(&chan_cfg, &ctx->tx_chan, &ctx->rx_chan);
  402. if (ret != ESP_OK) {
  403. ESP_LOGE(TAG, "i2s_new_channel failed: %s", esp_err_to_name(ret));
  404. goto err_free_bufs;
  405. }
  406. /* Map word_length to I2S bit depth */
  407. i2s_slot_mode_t slot_mode = I2S_SLOT_MODE_STEREO;
  408. /* 32-bit I2S: int32 maps directly to DMA, no conversion needed.
  409. * ES8311 supports 32-bit (SDP_IN_WL = 100). */
  410. i2s_std_config_t std_cfg = {
  411. .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(audio_config->sample_rate),
  412. .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_32BIT, slot_mode),
  413. };
  414. /* Use APLL clock source for precise audio frequency control.
  415. * APLL has SDM (Sigma-Delta Modulator) for sub-ppm tuning.
  416. * ESP32-P4 Ethernet uses MPLL, so no APLL conflict. */
  417. std_cfg.clk_cfg.clk_src = I2S_CLK_SRC_APLL;
  418. std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384;
  419. std_cfg.gpio_cfg = (i2s_std_gpio_config_t){
  420. .mclk = pins->mck_gpio,
  421. .bclk = pins->bck_gpio,
  422. .ws = pins->ws_gpio,
  423. .dout = pins->dout_gpio,
  424. .din = pins->din_gpio,
  425. .invert_flags = {
  426. .mclk_inv = false,
  427. .bclk_inv = false,
  428. .ws_inv = false,
  429. },
  430. };
  431. ret = i2s_channel_init_std_mode(ctx->tx_chan, &std_cfg);
  432. if (ret != ESP_OK) {
  433. ESP_LOGE(TAG, "i2s_channel_init_std_mode TX failed: %s", esp_err_to_name(ret));
  434. goto err_del_channel;
  435. }
  436. ret = i2s_channel_init_std_mode(ctx->rx_chan, &std_cfg);
  437. if (ret != ESP_OK) {
  438. ESP_LOGE(TAG, "i2s_channel_init_std_mode RX failed: %s", esp_err_to_name(ret));
  439. goto err_del_channel;
  440. }
  441. ctx->running = false;
  442. ctx->capture_wr = 0;
  443. ctx->capture_rd = 0;
  444. ctx->playback_wr = 0;
  445. ctx->playback_rd = 0;
  446. /* Staging ring: RTP RX writes int16, DMA ISR reads.
  447. * 10 packets of headroom to absorb network jitter and
  448. * phase differences between packet arrival and DMA consumption. */
  449. ctx->staging_size = ctx->frame_size * 10 * audio_config->channels;
  450. ctx->staging_buf = heap_caps_calloc(ctx->staging_size, sizeof(int16_t),
  451. MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
  452. if (!ctx->staging_buf) {
  453. ESP_LOGE(TAG, "Failed to allocate staging ring");
  454. goto err_del_channel;
  455. }
  456. ctx->staging_wr = 0;
  457. ctx->staging_rd = 0;
  458. ctx->staging_ready = false;
  459. /* Binary semaphore: DMA ISR signals when descriptor consumed */
  460. ctx->dma_sem = xSemaphoreCreateBinary();
  461. if (!ctx->dma_sem) {
  462. ESP_LOGE(TAG, "Failed to create DMA semaphore");
  463. goto err_del_channel;
  464. }
  465. /* Pre-allocate int16 conversion buffer for direct_write.
  466. * Size for max batch: 25 packets worth of frames. */
  467. ctx->i2s_conv_buf_samples = ctx->frame_size * 25 * audio_config->channels;
  468. ctx->i2s_conv_buf = heap_caps_malloc(
  469. ctx->i2s_conv_buf_samples * sizeof(int16_t),
  470. MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
  471. if (!ctx->i2s_conv_buf) {
  472. ESP_LOGE(TAG, "Failed to allocate I2S conversion buffer (%lu bytes)",
  473. (unsigned long)(ctx->i2s_conv_buf_samples * sizeof(int16_t)));
  474. goto err_del_channel;
  475. }
  476. *handle = ctx;
  477. ESP_LOGI(TAG, "Audio driver initialized (ring=%lu frames, dma=%lu bytes)",
  478. (unsigned long)ring_frames, (unsigned long)ctx->dma_buf_size_bytes);
  479. return ESP_OK;
  480. err_del_channel:
  481. i2s_del_channel(ctx->tx_chan);
  482. i2s_del_channel(ctx->rx_chan);
  483. err_free_bufs:
  484. heap_caps_free(ctx->capture_buf);
  485. heap_caps_free(ctx->playback_buf);
  486. heap_caps_free(ctx->dma_rx_buf);
  487. heap_caps_free(ctx->dma_tx_buf);
  488. free(ctx);
  489. return ESP_ERR_NO_MEM;
  490. }
  491. esp_err_t aes67_audio_start(aes67_audio_handle_t handle)
  492. {
  493. if (!handle) {
  494. return ESP_ERR_INVALID_ARG;
  495. }
  496. if (handle->running) {
  497. ESP_LOGW(TAG, "Audio already running");
  498. return ESP_OK;
  499. }
  500. /* Register DMA ISR callback for direct stream buffer -> DMA transfer.
  501. * This bypasses i2s_channel_write and the playback task entirely. */
  502. /* Register TX DMA ISR (playback: stream buffer -> I2S) */
  503. if ((s_isr_stream_buf != NULL)) {
  504. i2s_event_callbacks_t tx_cbs = {
  505. .on_recv = NULL,
  506. .on_recv_q_ovf = NULL,
  507. .on_sent = on_i2s_tx_sent_sbuf,
  508. .on_send_q_ovf = NULL,
  509. };
  510. esp_err_t cb_ret = i2s_channel_register_event_callback(
  511. handle->tx_chan, &tx_cbs, handle);
  512. if (cb_ret != ESP_OK) {
  513. ESP_LOGW(TAG, "Failed to register TX ISR: %s", esp_err_to_name(cb_ret));
  514. } else {
  515. ESP_LOGI(TAG, "TX DMA ISR registered (playback)");
  516. }
  517. }
  518. /* Register RX DMA ISR (capture: I2S -> stream buffer) */
  519. if (s_capture_stream_buf != NULL) {
  520. i2s_event_callbacks_t rx_cbs = {
  521. .on_recv = on_i2s_rx_recv,
  522. .on_recv_q_ovf = NULL,
  523. .on_sent = NULL,
  524. .on_send_q_ovf = NULL,
  525. };
  526. esp_err_t cb_ret = i2s_channel_register_event_callback(
  527. handle->rx_chan, &rx_cbs, handle);
  528. if (cb_ret != ESP_OK) {
  529. ESP_LOGW(TAG, "Failed to register RX ISR: %s", esp_err_to_name(cb_ret));
  530. } else {
  531. ESP_LOGI(TAG, "RX DMA ISR registered (capture)");
  532. }
  533. }
  534. /* Enable both TX and RX channels. The ES8311 codec requires both
  535. * to be active (matching the official ESP-IDF es8311 example). */
  536. esp_err_t ret = i2s_channel_enable(handle->tx_chan);
  537. if (ret != ESP_OK) {
  538. ESP_LOGE(TAG, "Failed to enable TX channel: %s", esp_err_to_name(ret));
  539. return ret;
  540. }
  541. ret = i2s_channel_enable(handle->rx_chan);
  542. if (ret != ESP_OK) {
  543. ESP_LOGW(TAG, "Failed to enable RX channel: %s", esp_err_to_name(ret));
  544. /* Non-fatal: TX still works */
  545. }
  546. handle->running = true;
  547. /* Read and cache the clock divider set by i2s_channel_init_std_mode.
  548. * This gives us the correct base values for adaptive clock recovery. */
  549. extern void aes67_audio_read_clock_divider(aes67_audio_handle_t h);
  550. aes67_audio_read_clock_divider(handle);
  551. ESP_LOGI(TAG, "Audio started (DMA: %d desc x %d frames, DMA-paced)",
  552. DMA_DESC_NUM, DMA_FRAME_NUM);
  553. return ESP_OK;
  554. }
  555. esp_err_t aes67_audio_stop(aes67_audio_handle_t handle)
  556. {
  557. if (!handle) {
  558. return ESP_ERR_INVALID_ARG;
  559. }
  560. if (!handle->running) {
  561. return ESP_OK;
  562. }
  563. handle->running = false;
  564. /* Disable I2S channels */
  565. i2s_channel_disable(handle->tx_chan);
  566. i2s_channel_disable(handle->rx_chan);
  567. ESP_LOGI(TAG, "Audio stopped");
  568. return ESP_OK;
  569. }
  570. esp_err_t aes67_audio_destroy(aes67_audio_handle_t handle)
  571. {
  572. if (!handle) {
  573. return ESP_ERR_INVALID_ARG;
  574. }
  575. /* Stop if still running */
  576. if (handle->running) {
  577. aes67_audio_stop(handle);
  578. }
  579. /* Delete I2S channels */
  580. i2s_del_channel(handle->tx_chan);
  581. i2s_del_channel(handle->rx_chan);
  582. /* Free all buffers */
  583. heap_caps_free(handle->capture_buf);
  584. heap_caps_free(handle->playback_buf);
  585. heap_caps_free(handle->dma_rx_buf);
  586. heap_caps_free(handle->dma_tx_buf);
  587. heap_caps_free(handle->i2s_conv_buf);
  588. free(handle);
  589. ESP_LOGI(TAG, "Audio driver destroyed");
  590. return ESP_OK;
  591. }
  592. esp_err_t aes67_audio_register_frame_cb(aes67_audio_handle_t handle,
  593. aes67_audio_frame_cb_t cb,
  594. void *user_data)
  595. {
  596. if (!handle) {
  597. return ESP_ERR_INVALID_ARG;
  598. }
  599. handle->frame_cb = cb;
  600. handle->frame_cb_user_data = user_data;
  601. return ESP_OK;
  602. }
  603. esp_err_t aes67_audio_write_playback(aes67_audio_handle_t handle,
  604. const void *samples, uint32_t frame_count)
  605. {
  606. if (!handle || !samples) {
  607. return ESP_ERR_INVALID_ARG;
  608. }
  609. const int32_t *src = (const int32_t *)samples;
  610. const uint8_t ch = handle->config.channels;
  611. const uint32_t cap = handle->buf_size_frames;
  612. uint32_t free_frames = ring_free(handle->playback_wr, handle->playback_rd, cap);
  613. if (frame_count > free_frames) {
  614. /* Not enough room. Just overwrite - the I2S read task will
  615. * pick up the latest data. Don't touch playback_rd here as
  616. * that would race with the I2S I/O task reading it. */
  617. frame_count = free_frames;
  618. if (frame_count == 0) return ESP_OK;
  619. }
  620. uint32_t wr = handle->playback_wr;
  621. for (uint32_t f = 0; f < frame_count; f++) {
  622. uint32_t ring_idx = (wr % cap) * ch;
  623. for (uint8_t c = 0; c < ch; c++) {
  624. handle->playback_buf[ring_idx + c] = src[f * ch + c];
  625. }
  626. wr++;
  627. }
  628. handle->playback_wr = wr;
  629. return ESP_OK;
  630. }
  631. esp_err_t aes67_audio_read_capture(aes67_audio_handle_t handle,
  632. void *samples, uint32_t frame_count)
  633. {
  634. if (!handle || !samples) {
  635. return ESP_ERR_INVALID_ARG;
  636. }
  637. int32_t *dst = (int32_t *)samples;
  638. const uint8_t ch = handle->config.channels;
  639. const uint32_t cap = handle->buf_size_frames;
  640. uint32_t avail = ring_available(handle->capture_wr, handle->capture_rd, cap);
  641. if (frame_count > avail) {
  642. return ESP_ERR_NOT_FOUND;
  643. }
  644. uint32_t rd = handle->capture_rd;
  645. for (uint32_t f = 0; f < frame_count; f++) {
  646. uint32_t ring_idx = (rd % cap) * ch;
  647. for (uint8_t c = 0; c < ch; c++) {
  648. dst[f * ch + c] = handle->capture_buf[ring_idx + c];
  649. }
  650. rd++;
  651. }
  652. handle->capture_rd = rd;
  653. return ESP_OK;
  654. }
  655. esp_err_t aes67_audio_direct_write(aes67_audio_handle_t handle,
  656. const int32_t *samples, uint32_t frame_count)
  657. {
  658. if (!handle || !samples || frame_count == 0) {
  659. return ESP_ERR_INVALID_ARG;
  660. }
  661. uint32_t total_samples = frame_count * handle->config.channels;
  662. /* Clamp to pre-allocated buffer capacity */
  663. if (total_samples > handle->i2s_conv_buf_samples) {
  664. total_samples = handle->i2s_conv_buf_samples;
  665. frame_count = total_samples / handle->config.channels;
  666. }
  667. /* With 24-bit I2S (32-bit slots), write int32 directly to DMA.
  668. * The L24 conversion already produces left-justified int32.
  669. * No truncation needed -- full 24-bit quality preserved. */
  670. size_t bytes_written = 0;
  671. size_t bytes_to_write = total_samples * sizeof(int32_t);
  672. int64_t t0 = esp_timer_get_time();
  673. esp_err_t ret = i2s_channel_write(handle->tx_chan, samples, bytes_to_write,
  674. &bytes_written, portMAX_DELAY);
  675. int64_t t1 = esp_timer_get_time();
  676. /* Measure i2s_channel_write timing to find the throughput bottleneck */
  677. static int64_t total_write_us = 0;
  678. static int64_t total_between_us = 0;
  679. static int64_t last_end_us = 0;
  680. if (last_end_us > 0) {
  681. total_between_us += (t0 - last_end_us);
  682. }
  683. last_end_us = t1;
  684. total_write_us += (t1 - t0);
  685. /* Log first few writes with sample values for debugging format issues */
  686. static uint32_t write_log_count = 0;
  687. if (write_log_count < 3) {
  688. write_log_count++;
  689. ESP_LOGI(TAG, "I2S write #%lu: %u/%u bytes, frames=%lu, ret=%s, "
  690. "write=%lldus, between=%lldus | "
  691. "int32[0..3]: %+ld %+ld %+ld %+ld -> "
  692. "int16[0..3]: %+d %+d %+d %+d",
  693. (unsigned long)write_log_count,
  694. (unsigned)bytes_written, (unsigned)bytes_to_write,
  695. (unsigned long)frame_count, esp_err_to_name(ret),
  696. (long long)(t1 - t0),
  697. (long long)(last_end_us > 0 ? (t0 - (last_end_us - (t1 - t0))) : 0),
  698. (long)samples[0], (long)samples[1],
  699. (long)samples[2], (long)samples[3],
  700. (long)samples[0], (long)samples[1],
  701. (long)samples[2], (long)samples[3]);
  702. }
  703. return ret;
  704. }
  705. esp_err_t aes67_audio_get_config(aes67_audio_handle_t handle,
  706. aes67_audio_config_t *config)
  707. {
  708. if (!handle || !config) {
  709. return ESP_ERR_INVALID_ARG;
  710. }
  711. memcpy(config, &handle->config, sizeof(aes67_audio_config_t));
  712. return ESP_OK;
  713. }
  714. esp_err_t aes67_audio_get_buffer_levels(aes67_audio_handle_t handle,
  715. uint32_t *capture_frames,
  716. uint32_t *playback_frames)
  717. {
  718. if (!handle) {
  719. return ESP_ERR_INVALID_ARG;
  720. }
  721. if (capture_frames) {
  722. *capture_frames = ring_available(handle->capture_wr, handle->capture_rd,
  723. handle->buf_size_frames);
  724. }
  725. if (playback_frames) {
  726. *playback_frames = ring_available(handle->playback_wr, handle->playback_rd,
  727. handle->buf_size_frames);
  728. }
  729. return ESP_OK;
  730. }
  731. /* Write int16 stereo samples to the staging ring buffer.
  732. * Called by the playback task. The DMA ISR reads from this ring. */
  733. esp_err_t aes67_audio_staging_write(aes67_audio_handle_t handle,
  734. const int16_t *samples, uint32_t sample_count)
  735. {
  736. if (!handle || !samples) return ESP_ERR_INVALID_ARG;
  737. uint32_t cap = handle->staging_size;
  738. uint32_t wr = handle->staging_wr;
  739. uint32_t rd = handle->staging_rd;
  740. uint32_t avail = (wr >= rd) ? (wr - rd) : (cap - rd + wr);
  741. uint32_t free_samples = cap - 1 - avail; /* -1 for sentinel */
  742. if (sample_count > free_samples) {
  743. sample_count = free_samples;
  744. }
  745. if (sample_count == 0) return ESP_OK;
  746. uint32_t pos = wr % cap;
  747. uint32_t first = cap - pos;
  748. if (first > sample_count) first = sample_count;
  749. memcpy(&handle->staging_buf[pos], samples, first * sizeof(int16_t));
  750. if (sample_count > first) {
  751. memcpy(handle->staging_buf, samples + first,
  752. (sample_count - first) * sizeof(int16_t));
  753. }
  754. handle->staging_wr = (wr + sample_count) % cap;
  755. /* Enable ISR reads once staging is at least half full (20ms).
  756. * More headroom = more tolerance for network jitter. */
  757. if (!handle->staging_ready) {
  758. uint32_t new_wr = handle->staging_wr;
  759. uint32_t cur_rd = handle->staging_rd;
  760. uint32_t filled = (new_wr >= cur_rd) ? (new_wr - cur_rd) : (cap - cur_rd + new_wr);
  761. if (filled >= cap / 2) {
  762. handle->staging_ready = true;
  763. }
  764. }
  765. return ESP_OK;
  766. }
  767. void aes67_audio_get_staging_stats(aes67_audio_handle_t handle,
  768. uint32_t *isr_count, uint32_t *underruns)
  769. {
  770. if (handle) {
  771. if (isr_count) *isr_count = handle->staging_isr_count;
  772. if (underruns) *underruns = handle->staging_underruns;
  773. }
  774. }
  775. /* Cached base divider values (read from registers after I2S init) */
  776. static uint32_t base_div_n = 0;
  777. static uint32_t base_div_x = 0;
  778. static uint32_t base_div_y = 0;
  779. static uint32_t base_div_z = 0;
  780. static uint32_t base_div_yn1 = 0;
  781. static uint32_t base_numerator = 0;
  782. static uint32_t base_denominator = 0;
  783. /* Read and cache the I2S0 TX clock divider from hardware registers.
  784. * Must be called AFTER i2s_channel_init_std_mode(). */
  785. void aes67_audio_read_clock_divider(aes67_audio_handle_t handle)
  786. {
  787. (void)handle;
  788. base_div_n = HP_SYS_CLKRST.peri_clk_ctrl13.reg_i2s0_tx_div_n;
  789. base_div_x = HP_SYS_CLKRST.peri_clk_ctrl13.reg_i2s0_tx_div_x;
  790. base_div_y = HP_SYS_CLKRST.peri_clk_ctrl14.reg_i2s0_tx_div_y;
  791. base_div_z = HP_SYS_CLKRST.peri_clk_ctrl14.reg_i2s0_tx_div_z;
  792. base_div_yn1 = HP_SYS_CLKRST.peri_clk_ctrl14.reg_i2s0_tx_div_yn1;
  793. /* Reverse the x,y,z,yn1 -> numerator,denominator conversion */
  794. if (base_div_z > 0) {
  795. base_denominator = (base_div_x + 1) * base_div_z + base_div_y;
  796. base_numerator = base_div_yn1 ? (base_denominator - base_div_z) : base_div_z;
  797. }
  798. ESP_LOGI("aes67_audio", "I2S0 TX clk: N=%lu, x=%lu, y=%lu, z=%lu, yn1=%lu "
  799. "-> numer=%lu denom=%lu (divider=N+%lu/%lu)",
  800. (unsigned long)base_div_n, (unsigned long)base_div_x,
  801. (unsigned long)base_div_y, (unsigned long)base_div_z,
  802. (unsigned long)base_div_yn1,
  803. (unsigned long)base_numerator, (unsigned long)base_denominator,
  804. (unsigned long)base_numerator, (unsigned long)base_denominator);
  805. }
  806. /* Adjust the I2S TX MCLK divider at runtime to fine-tune sample rate.
  807. * This is used for adaptive clock recovery: speed up or slow down the
  808. * I2S clock to match the network source rate.
  809. * ppm_adj: parts-per-million adjustment. +100 = 0.01% faster. */
  810. void aes67_audio_adjust_clock_ppm(aes67_audio_handle_t handle, int32_t ppm_adj)
  811. {
  812. if (!handle || base_denominator == 0) return;
  813. /* Adjust numerator relative to the cached base values.
  814. * Increasing numerator = larger divider = slower clock.
  815. * ppm_adj > 0 means "source is faster, speed up I2S" = decrease numerator. */
  816. int64_t adj = (int64_t)ppm_adj * (int64_t)base_denominator / 1000000;
  817. int32_t new_numer = (int32_t)base_numerator - (int32_t)adj;
  818. if (new_numer < 1) new_numer = 1;
  819. if (new_numer > (int32_t)base_denominator - 1) new_numer = base_denominator - 1;
  820. /* Convert numerator/denominator back to x,y,z,yn1 format */
  821. uint32_t numer = (uint32_t)new_numer;
  822. uint32_t denom = base_denominator;
  823. uint32_t div_yn1 = numer * 2 > denom;
  824. uint32_t div_z = div_yn1 ? denom - numer : numer;
  825. uint32_t div_x = div_z ? (denom / div_z - 1) : 0;
  826. uint32_t div_y = div_z ? (denom % div_z) : 0;
  827. /* Write to I2S0 TX clock divider registers.
  828. * Workaround: set small div first, then target. */
  829. HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl13,
  830. reg_i2s0_tx_div_n, 2);
  831. HP_SYS_CLKRST.peri_clk_ctrl14.reg_i2s0_tx_div_yn1 = 0;
  832. HP_SYS_CLKRST.peri_clk_ctrl14.reg_i2s0_tx_div_y = 1;
  833. HP_SYS_CLKRST.peri_clk_ctrl14.reg_i2s0_tx_div_z = 0;
  834. HP_SYS_CLKRST.peri_clk_ctrl13.reg_i2s0_tx_div_x = 0;
  835. /* Set target */
  836. HP_SYS_CLKRST.peri_clk_ctrl14.reg_i2s0_tx_div_yn1 = div_yn1;
  837. HP_SYS_CLKRST.peri_clk_ctrl14.reg_i2s0_tx_div_z = div_z;
  838. HP_SYS_CLKRST.peri_clk_ctrl14.reg_i2s0_tx_div_y = div_y;
  839. HP_SYS_CLKRST.peri_clk_ctrl13.reg_i2s0_tx_div_x = div_x;
  840. HAL_FORCE_MODIFY_U32_REG_FIELD(HP_SYS_CLKRST.peri_clk_ctrl13,
  841. reg_i2s0_tx_div_n, base_div_n);
  842. }
  843. SemaphoreHandle_t aes67_audio_get_dma_sem(aes67_audio_handle_t handle)
  844. {
  845. return handle ? handle->dma_sem : NULL;
  846. }
  847. i2s_chan_handle_t aes67_audio_get_tx_chan(aes67_audio_handle_t handle)
  848. {
  849. return handle ? handle->tx_chan : NULL;
  850. }