SDL_audioqueue.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #include "SDL_audioqueue.h"
  20. #include "SDL_sysaudio.h"
  21. typedef struct SDL_MemoryPool SDL_MemoryPool;
  22. struct SDL_MemoryPool
  23. {
  24. void *free_blocks;
  25. size_t block_size;
  26. size_t num_free;
  27. size_t max_free;
  28. };
  29. struct SDL_AudioTrack
  30. {
  31. SDL_AudioSpec spec;
  32. int *chmap;
  33. SDL_bool flushed;
  34. SDL_AudioTrack *next;
  35. void *userdata;
  36. SDL_ReleaseAudioBufferCallback callback;
  37. Uint8 *data;
  38. size_t head;
  39. size_t tail;
  40. size_t capacity;
  41. int chmap_storage[SDL_MAX_CHANNELMAP_CHANNELS]; // !!! FIXME: this needs to grow if SDL ever supports more channels. But if it grows, we should probably be more clever about allocations.
  42. };
  43. struct SDL_AudioQueue
  44. {
  45. SDL_AudioTrack *head;
  46. SDL_AudioTrack *tail;
  47. Uint8 *history_buffer;
  48. size_t history_length;
  49. size_t history_capacity;
  50. SDL_MemoryPool track_pool;
  51. SDL_MemoryPool chunk_pool;
  52. };
  53. // Allocate a new block, avoiding checking for ones already in the pool
  54. static void *AllocNewMemoryPoolBlock(const SDL_MemoryPool *pool)
  55. {
  56. return SDL_malloc(pool->block_size);
  57. }
  58. // Allocate a new block, first checking if there are any in the pool
  59. static void *AllocMemoryPoolBlock(SDL_MemoryPool *pool)
  60. {
  61. if (pool->num_free == 0) {
  62. return AllocNewMemoryPoolBlock(pool);
  63. }
  64. void *block = pool->free_blocks;
  65. pool->free_blocks = *(void **)block;
  66. --pool->num_free;
  67. return block;
  68. }
  69. // Free a block, or add it to the pool if there's room
  70. static void FreeMemoryPoolBlock(SDL_MemoryPool *pool, void *block)
  71. {
  72. if (pool->num_free < pool->max_free) {
  73. *(void **)block = pool->free_blocks;
  74. pool->free_blocks = block;
  75. ++pool->num_free;
  76. } else {
  77. SDL_free(block);
  78. }
  79. }
  80. // Destroy a pool and all of its blocks
  81. static void DestroyMemoryPool(SDL_MemoryPool *pool)
  82. {
  83. void *block = pool->free_blocks;
  84. pool->free_blocks = NULL;
  85. pool->num_free = 0;
  86. while (block) {
  87. void *next = *(void **)block;
  88. SDL_free(block);
  89. block = next;
  90. }
  91. }
  92. // Keeping a list of free chunks reduces memory allocations,
  93. // But also increases the amount of work to perform when freeing the track.
  94. static void InitMemoryPool(SDL_MemoryPool *pool, size_t block_size, size_t max_free)
  95. {
  96. SDL_zerop(pool);
  97. SDL_assert(block_size >= sizeof(void *));
  98. pool->block_size = block_size;
  99. pool->max_free = max_free;
  100. }
  101. // Allocates a number of blocks and adds them to the pool
  102. static int ReserveMemoryPoolBlocks(SDL_MemoryPool *pool, size_t num_blocks)
  103. {
  104. for (; num_blocks; --num_blocks) {
  105. void *block = AllocNewMemoryPoolBlock(pool);
  106. if (block == NULL) {
  107. return -1;
  108. }
  109. *(void **)block = pool->free_blocks;
  110. pool->free_blocks = block;
  111. ++pool->num_free;
  112. }
  113. return 0;
  114. }
  115. void SDL_DestroyAudioQueue(SDL_AudioQueue *queue)
  116. {
  117. SDL_ClearAudioQueue(queue);
  118. DestroyMemoryPool(&queue->track_pool);
  119. DestroyMemoryPool(&queue->chunk_pool);
  120. SDL_aligned_free(queue->history_buffer);
  121. SDL_free(queue);
  122. }
  123. SDL_AudioQueue *SDL_CreateAudioQueue(size_t chunk_size)
  124. {
  125. SDL_AudioQueue *queue = (SDL_AudioQueue *)SDL_calloc(1, sizeof(*queue));
  126. if (!queue) {
  127. return NULL;
  128. }
  129. InitMemoryPool(&queue->track_pool, sizeof(SDL_AudioTrack), 8);
  130. InitMemoryPool(&queue->chunk_pool, chunk_size, 4);
  131. if (ReserveMemoryPoolBlocks(&queue->track_pool, 2) != 0) {
  132. SDL_DestroyAudioQueue(queue);
  133. return NULL;
  134. }
  135. return queue;
  136. }
  137. static void DestroyAudioTrack(SDL_AudioQueue *queue, SDL_AudioTrack *track)
  138. {
  139. track->callback(track->userdata, track->data, (int)track->capacity);
  140. FreeMemoryPoolBlock(&queue->track_pool, track);
  141. }
  142. void SDL_ClearAudioQueue(SDL_AudioQueue *queue)
  143. {
  144. SDL_AudioTrack *track = queue->head;
  145. queue->head = NULL;
  146. queue->tail = NULL;
  147. queue->history_length = 0;
  148. while (track) {
  149. SDL_AudioTrack *next = track->next;
  150. DestroyAudioTrack(queue, track);
  151. track = next;
  152. }
  153. }
  154. static void FlushAudioTrack(SDL_AudioTrack *track)
  155. {
  156. track->flushed = SDL_TRUE;
  157. }
  158. void SDL_FlushAudioQueue(SDL_AudioQueue *queue)
  159. {
  160. SDL_AudioTrack *track = queue->tail;
  161. if (track) {
  162. FlushAudioTrack(track);
  163. }
  164. }
  165. void SDL_PopAudioQueueHead(SDL_AudioQueue *queue)
  166. {
  167. SDL_AudioTrack *track = queue->head;
  168. for (;;) {
  169. SDL_bool flushed = track->flushed;
  170. SDL_AudioTrack *next = track->next;
  171. DestroyAudioTrack(queue, track);
  172. track = next;
  173. if (flushed) {
  174. break;
  175. }
  176. }
  177. queue->head = track;
  178. queue->history_length = 0;
  179. if (!track) {
  180. queue->tail = NULL;
  181. }
  182. }
  183. SDL_AudioTrack *SDL_CreateAudioTrack(
  184. SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap,
  185. Uint8 *data, size_t len, size_t capacity,
  186. SDL_ReleaseAudioBufferCallback callback, void *userdata)
  187. {
  188. SDL_AudioTrack *track = AllocMemoryPoolBlock(&queue->track_pool);
  189. if (!track) {
  190. return NULL;
  191. }
  192. SDL_zerop(track);
  193. if (chmap) {
  194. SDL_assert(SDL_arraysize(track->chmap_storage) >= spec->channels);
  195. SDL_memcpy(track->chmap_storage, chmap, sizeof (*chmap) * spec->channels);
  196. track->chmap = track->chmap_storage;
  197. }
  198. SDL_copyp(&track->spec, spec);
  199. track->userdata = userdata;
  200. track->callback = callback;
  201. track->data = data;
  202. track->head = 0;
  203. track->tail = len;
  204. track->capacity = capacity;
  205. return track;
  206. }
  207. static void SDLCALL FreeChunkedAudioBuffer(void *userdata, const void *buf, int len)
  208. {
  209. SDL_AudioQueue *queue = userdata;
  210. FreeMemoryPoolBlock(&queue->chunk_pool, (void *)buf);
  211. }
  212. static SDL_AudioTrack *CreateChunkedAudioTrack(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap)
  213. {
  214. void *chunk = AllocMemoryPoolBlock(&queue->chunk_pool);
  215. if (!chunk) {
  216. return NULL;
  217. }
  218. size_t capacity = queue->chunk_pool.block_size;
  219. capacity -= capacity % SDL_AUDIO_FRAMESIZE(*spec);
  220. SDL_AudioTrack *track = SDL_CreateAudioTrack(queue, spec, chmap, chunk, 0, capacity, FreeChunkedAudioBuffer, queue);
  221. if (!track) {
  222. FreeMemoryPoolBlock(&queue->chunk_pool, chunk);
  223. return NULL;
  224. }
  225. return track;
  226. }
  227. void SDL_AddTrackToAudioQueue(SDL_AudioQueue *queue, SDL_AudioTrack *track)
  228. {
  229. SDL_AudioTrack *tail = queue->tail;
  230. if (tail) {
  231. // If the spec has changed, make sure to flush the previous track
  232. if (!SDL_AudioSpecsEqual(&tail->spec, &track->spec, tail->chmap, track->chmap)) {
  233. FlushAudioTrack(tail);
  234. }
  235. tail->next = track;
  236. } else {
  237. queue->head = track;
  238. }
  239. queue->tail = track;
  240. }
  241. static size_t WriteToAudioTrack(SDL_AudioTrack *track, const Uint8 *data, size_t len)
  242. {
  243. if (track->flushed || track->tail >= track->capacity) {
  244. return 0;
  245. }
  246. len = SDL_min(len, track->capacity - track->tail);
  247. SDL_memcpy(&track->data[track->tail], data, len);
  248. track->tail += len;
  249. return len;
  250. }
  251. int SDL_WriteToAudioQueue(SDL_AudioQueue *queue, const SDL_AudioSpec *spec, const int *chmap, const Uint8 *data, size_t len)
  252. {
  253. if (len == 0) {
  254. return 0;
  255. }
  256. SDL_AudioTrack *track = queue->tail;
  257. if (track) {
  258. if (!SDL_AudioSpecsEqual(&track->spec, spec, track->chmap, chmap)) {
  259. FlushAudioTrack(track);
  260. }
  261. } else {
  262. SDL_assert(!queue->head);
  263. track = CreateChunkedAudioTrack(queue, spec, chmap);
  264. if (!track) {
  265. return -1;
  266. }
  267. queue->head = track;
  268. queue->tail = track;
  269. }
  270. for (;;) {
  271. const size_t written = WriteToAudioTrack(track, data, len);
  272. data += written;
  273. len -= written;
  274. if (len == 0) {
  275. break;
  276. }
  277. SDL_AudioTrack *new_track = CreateChunkedAudioTrack(queue, spec, chmap);
  278. if (!new_track) {
  279. return -1;
  280. }
  281. track->next = new_track;
  282. queue->tail = new_track;
  283. track = new_track;
  284. }
  285. return 0;
  286. }
  287. void *SDL_BeginAudioQueueIter(SDL_AudioQueue *queue)
  288. {
  289. return queue->head;
  290. }
  291. size_t SDL_NextAudioQueueIter(SDL_AudioQueue *queue, void **inout_iter, SDL_AudioSpec *out_spec, int **out_chmap, SDL_bool *out_flushed)
  292. {
  293. SDL_AudioTrack *iter = (SDL_AudioTrack *)(*inout_iter);
  294. SDL_assert(iter != NULL);
  295. SDL_copyp(out_spec, &iter->spec);
  296. *out_chmap = iter->chmap;
  297. SDL_bool flushed = SDL_FALSE;
  298. size_t queued_bytes = 0;
  299. while (iter) {
  300. SDL_AudioTrack *track = iter;
  301. iter = iter->next;
  302. size_t avail = track->tail - track->head;
  303. if (avail >= SDL_SIZE_MAX - queued_bytes) {
  304. queued_bytes = SDL_SIZE_MAX;
  305. flushed = SDL_FALSE;
  306. break;
  307. }
  308. queued_bytes += avail;
  309. flushed = track->flushed;
  310. if (flushed) {
  311. break;
  312. }
  313. }
  314. *inout_iter = iter;
  315. *out_flushed = flushed;
  316. return queued_bytes;
  317. }
  318. static const Uint8 *PeekIntoAudioQueuePast(SDL_AudioQueue *queue, Uint8 *data, size_t len)
  319. {
  320. SDL_AudioTrack *track = queue->head;
  321. if (track->head >= len) {
  322. return &track->data[track->head - len];
  323. }
  324. size_t past = len - track->head;
  325. if (past > queue->history_length) {
  326. return NULL;
  327. }
  328. SDL_memcpy(data, &queue->history_buffer[queue->history_length - past], past);
  329. SDL_memcpy(&data[past], track->data, track->head);
  330. return data;
  331. }
  332. static void UpdateAudioQueueHistory(SDL_AudioQueue *queue,
  333. const Uint8 *data, size_t len)
  334. {
  335. Uint8 *history_buffer = queue->history_buffer;
  336. size_t history_bytes = queue->history_length;
  337. if (len >= history_bytes) {
  338. SDL_memcpy(history_buffer, &data[len - history_bytes], history_bytes);
  339. } else {
  340. size_t preserve = history_bytes - len;
  341. SDL_memmove(history_buffer, &history_buffer[len], preserve);
  342. SDL_memcpy(&history_buffer[preserve], data, len);
  343. }
  344. }
  345. static const Uint8 *ReadFromAudioQueue(SDL_AudioQueue *queue, Uint8 *data, size_t len)
  346. {
  347. SDL_AudioTrack *track = queue->head;
  348. if (track->tail - track->head >= len) {
  349. const Uint8 *ptr = &track->data[track->head];
  350. track->head += len;
  351. return ptr;
  352. }
  353. size_t total = 0;
  354. for (;;) {
  355. size_t avail = SDL_min(len - total, track->tail - track->head);
  356. SDL_memcpy(&data[total], &track->data[track->head], avail);
  357. track->head += avail;
  358. total += avail;
  359. if (total == len) {
  360. break;
  361. }
  362. if (track->flushed) {
  363. SDL_SetError("Reading past end of flushed track");
  364. return NULL;
  365. }
  366. SDL_AudioTrack *next = track->next;
  367. if (!next) {
  368. SDL_SetError("Reading past end of incomplete track");
  369. return NULL;
  370. }
  371. UpdateAudioQueueHistory(queue, track->data, track->tail);
  372. queue->head = next;
  373. DestroyAudioTrack(queue, track);
  374. track = next;
  375. }
  376. return data;
  377. }
  378. static const Uint8 *PeekIntoAudioQueueFuture(SDL_AudioQueue *queue, Uint8 *data, size_t len)
  379. {
  380. SDL_AudioTrack *track = queue->head;
  381. if (track->tail - track->head >= len) {
  382. return &track->data[track->head];
  383. }
  384. size_t total = 0;
  385. for (;;) {
  386. size_t avail = SDL_min(len - total, track->tail - track->head);
  387. SDL_memcpy(&data[total], &track->data[track->head], avail);
  388. total += avail;
  389. if (total == len) {
  390. break;
  391. }
  392. if (track->flushed) {
  393. // If we have run out of data, fill the rest with silence.
  394. SDL_memset(&data[total], SDL_GetSilenceValueForFormat(track->spec.format), len - total);
  395. break;
  396. }
  397. track = track->next;
  398. if (!track) {
  399. SDL_SetError("Peeking past end of incomplete track");
  400. return NULL;
  401. }
  402. }
  403. return data;
  404. }
  405. const Uint8 *SDL_ReadFromAudioQueue(SDL_AudioQueue *queue,
  406. Uint8 *dst, SDL_AudioFormat dst_format, int dst_channels, const int *dst_map,
  407. int past_frames, int present_frames, int future_frames,
  408. Uint8 *scratch, float gain)
  409. {
  410. SDL_AudioTrack *track = queue->head;
  411. if (!track) {
  412. return NULL;
  413. }
  414. SDL_AudioFormat src_format = track->spec.format;
  415. int src_channels = track->spec.channels;
  416. const int *src_map = track->chmap;
  417. size_t src_frame_size = SDL_AUDIO_BYTESIZE(src_format) * src_channels;
  418. size_t dst_frame_size = SDL_AUDIO_BYTESIZE(dst_format) * dst_channels;
  419. size_t src_past_bytes = past_frames * src_frame_size;
  420. size_t src_present_bytes = present_frames * src_frame_size;
  421. size_t src_future_bytes = future_frames * src_frame_size;
  422. size_t dst_past_bytes = past_frames * dst_frame_size;
  423. size_t dst_present_bytes = present_frames * dst_frame_size;
  424. size_t dst_future_bytes = future_frames * dst_frame_size;
  425. SDL_bool convert = (src_format != dst_format) || (src_channels != dst_channels);
  426. if (convert && !dst) {
  427. // The user didn't ask for the data to be copied, but we need to convert it, so store it in the scratch buffer
  428. dst = scratch;
  429. }
  430. // Can we get all of the data straight from this track?
  431. if ((track->head >= src_past_bytes) && ((track->tail - track->head) >= (src_present_bytes + src_future_bytes))) {
  432. const Uint8 *ptr = &track->data[track->head - src_past_bytes];
  433. track->head += src_present_bytes;
  434. // Do we still need to copy/convert the data?
  435. if (dst) {
  436. ConvertAudio(past_frames + present_frames + future_frames, ptr,
  437. src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain);
  438. ptr = dst;
  439. }
  440. return ptr;
  441. }
  442. if (!dst) {
  443. // The user didn't ask for the data to be copied, but we need to, so store it in the scratch buffer
  444. dst = scratch;
  445. } else if (!convert) {
  446. // We are only copying, not converting, so copy straight into the dst buffer
  447. scratch = dst;
  448. }
  449. Uint8 *ptr = dst;
  450. if (src_past_bytes) {
  451. ConvertAudio(past_frames, PeekIntoAudioQueuePast(queue, scratch, src_past_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain);
  452. dst += dst_past_bytes;
  453. scratch += dst_past_bytes;
  454. }
  455. if (src_present_bytes) {
  456. ConvertAudio(present_frames, ReadFromAudioQueue(queue, scratch, src_present_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain);
  457. dst += dst_present_bytes;
  458. scratch += dst_present_bytes;
  459. }
  460. if (src_future_bytes) {
  461. ConvertAudio(future_frames, PeekIntoAudioQueueFuture(queue, scratch, src_future_bytes), src_format, src_channels, src_map, dst, dst_format, dst_channels, dst_map, scratch, gain);
  462. dst += dst_future_bytes;
  463. scratch += dst_future_bytes;
  464. }
  465. return ptr;
  466. }
  467. size_t SDL_GetAudioQueueQueued(SDL_AudioQueue *queue)
  468. {
  469. size_t total = 0;
  470. void *iter = SDL_BeginAudioQueueIter(queue);
  471. while (iter) {
  472. SDL_AudioSpec src_spec;
  473. int *src_chmap;
  474. SDL_bool flushed;
  475. size_t avail = SDL_NextAudioQueueIter(queue, &iter, &src_spec, &src_chmap, &flushed);
  476. if (avail >= SDL_SIZE_MAX - total) {
  477. total = SDL_SIZE_MAX;
  478. break;
  479. }
  480. total += avail;
  481. }
  482. return total;
  483. }
  484. int SDL_ResetAudioQueueHistory(SDL_AudioQueue *queue, int num_frames)
  485. {
  486. SDL_AudioTrack *track = queue->head;
  487. if (!track) {
  488. return -1;
  489. }
  490. size_t length = num_frames * SDL_AUDIO_FRAMESIZE(track->spec);
  491. Uint8 *history_buffer = queue->history_buffer;
  492. if (queue->history_capacity < length) {
  493. history_buffer = SDL_aligned_alloc(SDL_GetSIMDAlignment(), length);
  494. if (!history_buffer) {
  495. return -1;
  496. }
  497. SDL_aligned_free(queue->history_buffer);
  498. queue->history_buffer = history_buffer;
  499. queue->history_capacity = length;
  500. }
  501. queue->history_length = length;
  502. SDL_memset(history_buffer, SDL_GetSilenceValueForFormat(track->spec.format), length);
  503. return 0;
  504. }