audio_buffer.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include "audio_buffer.h"
  4. #include "esp_heap_caps.h"
  5. #include "esp_log.h"
  6. static const char *TAG = "audio_buf";
  7. /* ---------- helpers for the slot pool ---------- */
  8. static inline uint8_t *slot_ptr(audio_buffer_t *b, uint16_t slot) {
  9. return b->pool + (size_t)slot * b->slot_size;
  10. }
  11. static inline uint32_t slot_timestamp(audio_buffer_t *b, uint16_t slot) {
  12. return ((audio_frame_header_t *)slot_ptr(b, slot))->rtp_timestamp;
  13. }
  14. /* RTP timestamp comparison that handles 32-bit wraparound.
  15. Returns negative if a < b, 0 if equal, positive if a > b. */
  16. static inline int32_t ts_cmp(uint32_t a, uint32_t b) {
  17. return (int32_t)(a - b);
  18. }
  19. /* Binary search: find the index in sorted[] where a frame with `timestamp`
  20. should be inserted to keep ascending order. */
  21. static int sorted_insert_pos(audio_buffer_t *b, uint32_t timestamp) {
  22. int lo = 0, hi = b->count;
  23. while (lo < hi) {
  24. int mid = lo + (hi - lo) / 2;
  25. if (ts_cmp(slot_timestamp(b, b->sorted[mid]), timestamp) < 0) {
  26. lo = mid + 1;
  27. } else {
  28. hi = mid;
  29. }
  30. }
  31. return lo;
  32. }
  33. /* ---------- queue_chunk (insert) ---------- */
  34. static bool audio_buffer_queue_chunk(audio_buffer_t *buffer,
  35. audio_stats_t *stats, uint32_t timestamp,
  36. const int16_t *pcm_data, size_t samples,
  37. int channels) {
  38. if (samples == 0) {
  39. return false;
  40. }
  41. portENTER_CRITICAL(&buffer->lock);
  42. /* Overflow protection: drain oldest frames if at capacity */
  43. while (buffer->count >= buffer->capacity && buffer->count > 0) {
  44. uint16_t victim = buffer->sorted[0];
  45. memmove(&buffer->sorted[0], &buffer->sorted[1],
  46. (buffer->count - 1) * sizeof(uint16_t));
  47. buffer->count--;
  48. buffer->free_stack[buffer->free_top++] = victim;
  49. /* Take one token from the semaphore to keep it in sync */
  50. xSemaphoreTakeFromISR(buffer->data_ready, NULL);
  51. }
  52. if (buffer->free_top == 0) {
  53. portEXIT_CRITICAL(&buffer->lock);
  54. if (stats) {
  55. stats->buffer_underruns++;
  56. }
  57. return false;
  58. }
  59. /* Pop a free slot */
  60. uint16_t slot = buffer->free_stack[--buffer->free_top];
  61. /* Build frame into pool slot */
  62. uint8_t *dest = slot_ptr(buffer, slot);
  63. audio_frame_header_t *hdr = (audio_frame_header_t *)dest;
  64. hdr->rtp_timestamp = timestamp;
  65. hdr->samples_per_channel = (uint16_t)samples;
  66. hdr->channels = (uint8_t)channels;
  67. hdr->reserved = 0;
  68. size_t pcm_bytes = samples * channels * sizeof(int16_t);
  69. memcpy(dest + sizeof(audio_frame_header_t), pcm_data, pcm_bytes);
  70. /* Binary search for insertion position */
  71. int pos = sorted_insert_pos(buffer, timestamp);
  72. /* Shift indices to make room */
  73. if (pos < buffer->count) {
  74. memmove(&buffer->sorted[pos + 1], &buffer->sorted[pos],
  75. (buffer->count - pos) * sizeof(uint16_t));
  76. }
  77. buffer->sorted[pos] = slot;
  78. buffer->count++;
  79. portEXIT_CRITICAL(&buffer->lock);
  80. /* Signal consumer */
  81. xSemaphoreGive(buffer->data_ready);
  82. if (stats) {
  83. stats->packets_decoded++;
  84. }
  85. return true;
  86. }
  87. /* ---------- init / deinit ---------- */
  88. esp_err_t audio_buffer_init(audio_buffer_t *buffer) {
  89. if (!buffer) {
  90. return ESP_ERR_INVALID_ARG;
  91. }
  92. memset(buffer, 0, sizeof(*buffer));
  93. portMUX_TYPE lock = portMUX_INITIALIZER_UNLOCKED;
  94. buffer->lock = lock;
  95. buffer->capacity = MAX_RING_BUFFER_FRAMES;
  96. buffer->slot_size = BYTES_PER_FRAME;
  97. buffer->count = 0;
  98. /* Pool in PSRAM */
  99. buffer->pool =
  100. (uint8_t *)heap_caps_malloc((size_t)buffer->capacity * buffer->slot_size,
  101. MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
  102. if (!buffer->pool) {
  103. ESP_LOGE(TAG, "Failed to allocate pool in PSRAM");
  104. return ESP_ERR_NO_MEM;
  105. }
  106. /* Sorted index array + free stack (internal RAM is fine, they're small) */
  107. buffer->sorted = (uint16_t *)malloc(buffer->capacity * sizeof(uint16_t));
  108. buffer->free_stack = (uint16_t *)malloc(buffer->capacity * sizeof(uint16_t));
  109. if (!buffer->sorted || !buffer->free_stack) {
  110. ESP_LOGE(TAG, "Failed to allocate index arrays");
  111. audio_buffer_deinit(buffer);
  112. return ESP_ERR_NO_MEM;
  113. }
  114. /* Initialise free stack: all slots available */
  115. buffer->free_top = buffer->capacity;
  116. for (int i = 0; i < buffer->capacity; i++) {
  117. buffer->free_stack[i] = (uint16_t)i;
  118. }
  119. /* Counting semaphore: max = capacity, initial = 0 */
  120. buffer->data_ready = xSemaphoreCreateCounting(buffer->capacity, 0);
  121. if (!buffer->data_ready) {
  122. ESP_LOGE(TAG, "Failed to create semaphore");
  123. audio_buffer_deinit(buffer);
  124. return ESP_ERR_NO_MEM;
  125. }
  126. /* Temp assembly / decode buffer (same as before) */
  127. size_t max_pcm_bytes =
  128. (size_t)MAX_SAMPLES_PER_FRAME * AUDIO_MAX_CHANNELS * sizeof(int16_t);
  129. buffer->frame_buffer =
  130. (uint8_t *)malloc(sizeof(audio_frame_header_t) + max_pcm_bytes);
  131. if (!buffer->frame_buffer) {
  132. ESP_LOGE(TAG, "Failed to allocate frame buffer");
  133. audio_buffer_deinit(buffer);
  134. return ESP_ERR_NO_MEM;
  135. }
  136. buffer->decode_buffer =
  137. (int16_t *)(buffer->frame_buffer + sizeof(audio_frame_header_t));
  138. buffer->decode_capacity_samples = MAX_SAMPLES_PER_FRAME;
  139. ESP_LOGI(TAG, "Sorted buffer created: %d slots × %zu bytes = %zu bytes",
  140. buffer->capacity, buffer->slot_size,
  141. (size_t)buffer->capacity * buffer->slot_size);
  142. return ESP_OK;
  143. }
  144. void audio_buffer_deinit(audio_buffer_t *buffer) {
  145. if (!buffer) {
  146. return;
  147. }
  148. if (buffer->data_ready) {
  149. vSemaphoreDelete(buffer->data_ready);
  150. buffer->data_ready = NULL;
  151. }
  152. if (buffer->pool) {
  153. heap_caps_free(buffer->pool);
  154. buffer->pool = NULL;
  155. }
  156. free(buffer->sorted);
  157. buffer->sorted = NULL;
  158. free(buffer->free_stack);
  159. buffer->free_stack = NULL;
  160. if (buffer->frame_buffer) {
  161. free(buffer->frame_buffer);
  162. buffer->frame_buffer = NULL;
  163. buffer->decode_buffer = NULL;
  164. buffer->decode_capacity_samples = 0;
  165. }
  166. buffer->count = 0;
  167. buffer->free_top = 0;
  168. }
  169. /* ---------- flush ---------- */
  170. void audio_buffer_flush(audio_buffer_t *buffer) {
  171. if (!buffer || !buffer->pool) {
  172. return;
  173. }
  174. portENTER_CRITICAL(&buffer->lock);
  175. /* Return all active slots to free stack */
  176. for (int i = 0; i < buffer->count; i++) {
  177. buffer->free_stack[buffer->free_top++] = buffer->sorted[i];
  178. }
  179. buffer->count = 0;
  180. portEXIT_CRITICAL(&buffer->lock);
  181. /* Drain the semaphore */
  182. while (xSemaphoreTake(buffer->data_ready, 0) == pdTRUE) {
  183. }
  184. }
  185. /* ---------- frame count ---------- */
  186. int audio_buffer_get_frame_count(audio_buffer_t *buffer) {
  187. if (!buffer) {
  188. return 0;
  189. }
  190. int frames = 0;
  191. portENTER_CRITICAL(&buffer->lock);
  192. frames = buffer->count;
  193. portEXIT_CRITICAL(&buffer->lock);
  194. return frames;
  195. }
  196. /* ---------- nearly full check (for back-pressure) ---------- */
  197. bool audio_buffer_is_nearly_full(audio_buffer_t *buffer) {
  198. if (!buffer) {
  199. return false;
  200. }
  201. int count = 0;
  202. portENTER_CRITICAL(&buffer->lock);
  203. count = buffer->count;
  204. portEXIT_CRITICAL(&buffer->lock);
  205. // Consider buffer "nearly full" when > 90% capacity
  206. return count > (buffer->capacity * 9 / 10);
  207. }
  208. /* ---------- take (consumer) ---------- */
  209. bool audio_buffer_take(audio_buffer_t *buffer, void **item, size_t *item_size,
  210. TickType_t ticks) {
  211. if (!buffer || !buffer->pool || !item || !item_size) {
  212. return false;
  213. }
  214. /* Block until a frame is available */
  215. if (xSemaphoreTake(buffer->data_ready, ticks) != pdTRUE) {
  216. return false;
  217. }
  218. portENTER_CRITICAL(&buffer->lock);
  219. if (buffer->count == 0) {
  220. /* Shouldn't happen if semaphore is in sync, but guard anyway */
  221. portEXIT_CRITICAL(&buffer->lock);
  222. return false;
  223. }
  224. uint16_t slot = buffer->sorted[0];
  225. /* Shift remaining indices left */
  226. if (buffer->count > 1) {
  227. memmove(&buffer->sorted[0], &buffer->sorted[1],
  228. (buffer->count - 1) * sizeof(uint16_t));
  229. }
  230. buffer->count--;
  231. portEXIT_CRITICAL(&buffer->lock);
  232. uint8_t *ptr = slot_ptr(buffer, slot);
  233. audio_frame_header_t *hdr = (audio_frame_header_t *)ptr;
  234. *item = ptr;
  235. *item_size = sizeof(audio_frame_header_t) + (size_t)hdr->samples_per_channel *
  236. hdr->channels *
  237. sizeof(int16_t);
  238. return true;
  239. }
  240. /* ---------- return (consumer gives back slot) ---------- */
  241. void audio_buffer_return(audio_buffer_t *buffer, void *item) {
  242. if (!buffer || !buffer->pool || !item) {
  243. return;
  244. }
  245. uint16_t slot =
  246. (uint16_t)(((uint8_t *)item - buffer->pool) / buffer->slot_size);
  247. portENTER_CRITICAL(&buffer->lock);
  248. buffer->free_stack[buffer->free_top++] = slot;
  249. portEXIT_CRITICAL(&buffer->lock);
  250. }
  251. /* ---------- decode buffer accessor ---------- */
  252. int16_t *audio_buffer_get_decode_buffer(audio_buffer_t *buffer,
  253. size_t *capacity_samples) {
  254. if (!buffer) {
  255. return NULL;
  256. }
  257. if (capacity_samples) {
  258. *capacity_samples = buffer->decode_capacity_samples;
  259. }
  260. return buffer->decode_buffer;
  261. }
  262. /* ---------- oldest timestamp peek ---------- */
  263. bool audio_buffer_oldest_timestamp(audio_buffer_t *buffer,
  264. uint32_t *timestamp) {
  265. if (!buffer || !buffer->pool || !timestamp) {
  266. return false;
  267. }
  268. portENTER_CRITICAL(&buffer->lock);
  269. if (buffer->count == 0) {
  270. portEXIT_CRITICAL(&buffer->lock);
  271. return false;
  272. }
  273. *timestamp = slot_timestamp(buffer, buffer->sorted[0]);
  274. portEXIT_CRITICAL(&buffer->lock);
  275. return true;
  276. }
  277. /* ---------- queue decoded (splits large frames into chunks) ---------- */
  278. bool audio_buffer_queue_decoded(audio_buffer_t *buffer, audio_stats_t *stats,
  279. uint32_t timestamp, const int16_t *pcm_data,
  280. size_t samples, int channels) {
  281. if (!buffer || !pcm_data || samples == 0) {
  282. return false;
  283. }
  284. if (channels <= 0) {
  285. channels = 2;
  286. }
  287. size_t offset = 0;
  288. uint32_t chunk_timestamp = timestamp;
  289. while (offset < samples) {
  290. size_t chunk_samples = samples - offset;
  291. if (chunk_samples > AAC_FRAMES_PER_PACKET) {
  292. chunk_samples = AAC_FRAMES_PER_PACKET;
  293. }
  294. if (!audio_buffer_queue_chunk(buffer, stats, chunk_timestamp,
  295. pcm_data + (offset * channels), chunk_samples,
  296. channels)) {
  297. return false;
  298. }
  299. offset += chunk_samples;
  300. chunk_timestamp += chunk_samples;
  301. }
  302. return true;
  303. }