audio_i2s.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. /**
  2. * Copyright (C) 2023 saybur
  3. * ZuluSCSI™ - Copyright (c) 2023-2025 Rabbit Hole Computing™
  4. *
  5. * ZuluSCSI™ firmware is licensed under the GPL version 3 or any later version. 
  6. *
  7. * https://www.gnu.org/licenses/gpl-3.0.html
  8. * ----
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version. 
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17. * GNU General Public License for more details. 
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  21. **/
  22. #ifdef ENABLE_AUDIO_OUTPUT_I2S
  23. #include "audio_i2s.h"
  24. #include "ZuluSCSI_platform.h"
  25. #include "ZuluSCSI_audio.h"
  26. #include "ZuluSCSI_v1_1_gpio.h"
  27. #include "ZuluSCSI_log.h"
  28. extern "C"
  29. {
  30. #include "gd32f20x_rcu.h"
  31. #include "gd32f20x_dma.h"
  32. #include "gd32f20x_misc.h"
  33. }
  34. bool g_audio_enabled = false;
  35. bool g_audio_stopped = true;
  36. // some chonky buffers to store audio samples
  37. static uint8_t sample_circ_buf[AUDIO_BUFFER_SIZE] __attribute__((aligned(4)));
  38. // tracking for the state for the circular buffer, A first half and B second half
  39. enum bufstate { STALE, FILLING, READY };
  40. static volatile bufstate sbufst_a = STALE;
  41. static volatile bufstate sbufst_b = STALE;
  42. static uint8_t sbufswap = 0;
  43. // tracking for audio playback
  44. static uint8_t audio_owner; // SCSI ID or 0xFF when idle
  45. static volatile bool audio_paused = false;
  46. static ImageBackingStore* audio_file;
  47. static volatile uint64_t fpos;
  48. static volatile uint32_t fleft;
  49. extern bool g_audio_stopped;
  50. // historical playback status information
  51. static audio_status_code audio_last_status[8] = {ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS,
  52. ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS, ASC_NO_STATUS};
  53. // volume information for targets
  54. static volatile uint16_t volumes[8] = {
  55. DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL,
  56. DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL
  57. };
  58. static volatile uint16_t channels[8] = {
  59. AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK,
  60. AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK
  61. };
  62. bool audio_is_active() {
  63. return audio_owner != 0xFF;
  64. }
  65. bool audio_is_playing(uint8_t id) {
  66. return audio_owner == (id & 7);
  67. }
  68. static void audio_start_dma()
  69. {
  70. dma_channel_disable(ODE_DMA, ODE_DMA_CH);
  71. dma_interrupt_flag_clear(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_HTF);
  72. dma_interrupt_flag_clear(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_FTF);
  73. dma_interrupt_enable(ODE_DMA, ODE_DMA_CH, DMA_INT_HTF | DMA_INT_FTF);
  74. dma_transfer_number_config(ODE_DMA, ODE_DMA_CH, AUDIO_BUFFER_SIZE / 2); // convert to 16bit transfer count
  75. spi_enable(ODE_I2S_SPI);
  76. dma_channel_enable(ODE_DMA, ODE_DMA_CH);
  77. }
  78. extern "C"
  79. {
  80. void ODE_IRQHandler()
  81. {
  82. if ( SET == dma_interrupt_flag_get(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_HTF))
  83. {
  84. dma_interrupt_flag_clear(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_HTF);
  85. sbufst_a = STALE;
  86. }
  87. if (SET == dma_interrupt_flag_get(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_FTF))
  88. {
  89. dma_interrupt_flag_clear(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_FTF);
  90. sbufst_b = STALE;
  91. }
  92. }
  93. }
  94. void audio_setup()
  95. {
  96. // Setup clocks
  97. rcu_periph_clock_enable(ODE_RCU_I2S_SPI);
  98. rcu_periph_clock_enable(ODE_RCU_DMA);
  99. // Install NVIC
  100. nvic_irq_enable(ODE_DMA_IRQn, 3, 0);
  101. // DMA setup
  102. dma_parameter_struct dma_init_struct;
  103. dma_struct_para_init(&dma_init_struct);
  104. dma_deinit(ODE_DMA, ODE_DMA_CH);
  105. dma_init_struct.periph_addr = (uint32_t)&SPI_DATA(ODE_I2S_SPI);
  106. dma_init_struct.memory_addr = (uint32_t)sample_circ_buf;
  107. dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
  108. dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
  109. dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
  110. dma_init_struct.priority = DMA_PRIORITY_LOW;
  111. dma_init_struct.number = AUDIO_BUFFER_SIZE / 2; // 8 bit to 16 bit conversion length
  112. dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
  113. dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
  114. dma_init(ODE_DMA, ODE_DMA_CH, &dma_init_struct);
  115. /* configure DMA mode */
  116. dma_circulation_enable(ODE_DMA, ODE_DMA_CH);
  117. dma_memory_to_memory_disable(ODE_DMA, ODE_DMA_CH);
  118. /* configure I2S1 */
  119. i2s_disable(ODE_I2S_SPI);
  120. spi_i2s_deinit(ODE_I2S_SPI);
  121. i2s_init(ODE_I2S_SPI, I2S_MODE_MASTERTX, I2S_STD_PHILLIPS, I2S_CKPL_LOW);
  122. i2s_psc_config(ODE_I2S_SPI, I2S_AUDIOSAMPLE_44K, I2S_FRAMEFORMAT_DT16B_CH16B, I2S_MCKOUT_DISABLE);
  123. spi_dma_enable(ODE_I2S_SPI, SPI_DMA_TRANSMIT);
  124. i2s_enable(ODE_I2S_SPI);
  125. }
  126. /*
  127. * Takes in a buffer with interleaved 16bit samples and adjusts their volume
  128. */
  129. static void audio_adjust(uint8_t owner, int16_t* buffer, size_t length)
  130. {
  131. uint8_t volume[2];
  132. uint16_t packed_volume = volumes[owner & 7];
  133. volume[0] = packed_volume >> 8;
  134. volume[1] = packed_volume & 0xFF;
  135. // enable or disable based on the channel information for both output
  136. // ports, where the high byte and mask control the right channel, and
  137. // the low control the left channel
  138. uint16_t chn = channels[owner & 7] & AUDIO_CHANNEL_ENABLE_MASK;
  139. if (!(chn >> 8))
  140. {
  141. volume[0] = 0;
  142. }
  143. if (!(chn & 0xFF))
  144. {
  145. volume[1] = 0;
  146. }
  147. for (int i = 0; i < length; i++)
  148. {
  149. // linear volume
  150. buffer[i] = (int16_t)(( ((int32_t)buffer[i]) * volume[i & 0x1]) >> 8);
  151. }
  152. }
  153. void audio_poll()
  154. {
  155. if(!g_audio_enabled)
  156. return;
  157. if ((!g_audio_stopped) && 1 == (0x1 & platform_get_buttons()))
  158. {
  159. audio_stop(audio_owner);
  160. }
  161. if (!audio_is_active()) return;
  162. if (audio_paused) return;
  163. if (fleft == 0 && sbufst_a == STALE && sbufst_b == STALE) {
  164. // out of data and ready to stop
  165. audio_stop(audio_owner);
  166. return;
  167. } else if (fleft == 0) {
  168. // out of data to read but still working on remainder
  169. return;
  170. } else if (!audio_file->isOpen()) {
  171. // closed elsewhere, maybe disk ejected?
  172. dbgmsg("------ Playback stop due to closed file");
  173. audio_stop(audio_owner);
  174. return;
  175. }
  176. if ( STALE == sbufst_a || STALE == sbufst_b )
  177. {
  178. uint8_t* audiobuf;
  179. if (sbufst_a == STALE) {
  180. sbufst_a = FILLING;
  181. audiobuf = sample_circ_buf;
  182. }
  183. if (sbufst_b == STALE) {
  184. sbufst_b = FILLING;
  185. audiobuf = sample_circ_buf + AUDIO_BUFFER_HALF_SIZE;
  186. }
  187. platform_set_sd_callback(NULL, NULL);
  188. uint16_t toRead = AUDIO_BUFFER_HALF_SIZE;
  189. if (fleft < toRead) toRead = fleft;
  190. if (audio_file->position() != fpos) {
  191. // should be uncommon due to SCSI command restrictions on devices
  192. // playing audio; if this is showing up in logs a different approach
  193. // will be needed to avoid seek performance issues on FAT32 vols
  194. dbgmsg("------ Audio seek required on ", audio_owner);
  195. if (!audio_file->seek(fpos)) {
  196. logmsg("Audio error, unable to seek to ", fpos, ", ID:", audio_owner);
  197. }
  198. }
  199. ssize_t read_length = audio_file->read(audiobuf, AUDIO_BUFFER_HALF_SIZE);
  200. if ( read_length < AUDIO_BUFFER_HALF_SIZE )
  201. {
  202. if ( read_length < 0)
  203. {
  204. logmsg("Playback file half buffer size read error: ", read_length);
  205. return;
  206. }
  207. else
  208. {
  209. // pad buffer with zeros
  210. memset(audiobuf + read_length, 0, AUDIO_BUFFER_HALF_SIZE - read_length);
  211. }
  212. }
  213. audio_adjust(audio_owner, (int16_t*) audiobuf, AUDIO_BUFFER_HALF_SIZE / 2);
  214. fpos += toRead;
  215. fleft -= toRead;
  216. if (sbufst_a == FILLING) {
  217. sbufst_a = READY;
  218. } else if (sbufst_b == FILLING) {
  219. sbufst_b = READY;
  220. }
  221. }
  222. }
  223. bool audio_play(uint8_t owner, image_config_t* img, uint64_t start, uint64_t end, bool swap)
  224. {
  225. if (audio_is_active()) audio_stop(audio_owner);
  226. // verify audio file is present and inputs are (somewhat) sane
  227. if (owner == 0xFF)
  228. {
  229. logmsg("Illegal audio owner");
  230. return false;
  231. }
  232. if (start >= end)
  233. {
  234. logmsg("Invalid range for audio (", start, ":", end, ")");
  235. return false;
  236. }
  237. audio_file = &img->file;
  238. if (!audio_file->isOpen()) {
  239. logmsg("File not open for audio playback, ", owner);
  240. return false;
  241. }
  242. uint64_t len = audio_file->size();
  243. if (start > len)
  244. {
  245. logmsg("File playback request start (", start, ":", len, ") outside file bounds");
  246. return false;
  247. }
  248. // truncate playback end to end of file
  249. // we will not consider this to be an error at the moment
  250. if (end > len)
  251. {
  252. dbgmsg("------ Truncate audio play request end ", end, " to file size ", len);
  253. end = len;
  254. }
  255. fleft = end - start;
  256. if (fleft <= AUDIO_BUFFER_SIZE)
  257. {
  258. logmsg("File playback request (", start, ":", end, ") too short");
  259. return false;
  260. }
  261. // read in initial sample buffers
  262. if (!audio_file->seek(start))
  263. {
  264. logmsg("Sample file failed start seek to ", start);
  265. return false;
  266. }
  267. ssize_t read_length = audio_file->read(sample_circ_buf, AUDIO_BUFFER_SIZE);
  268. if ( read_length < AUDIO_BUFFER_SIZE)
  269. {
  270. if ( read_length < 0)
  271. {
  272. logmsg("Playback file read error: ", read_length);
  273. return false;
  274. }
  275. else
  276. {
  277. // pad buffer with zeros
  278. memset(sample_circ_buf + read_length, 0, AUDIO_BUFFER_SIZE - read_length);
  279. }
  280. }
  281. audio_adjust(owner, (int16_t*)sample_circ_buf, AUDIO_BUFFER_SIZE / 2);
  282. // prepare initial tracking state
  283. fpos = audio_file->position();
  284. fleft -= AUDIO_BUFFER_SIZE;
  285. sbufswap = swap;
  286. sbufst_a = READY;
  287. sbufst_b = READY;
  288. audio_owner = owner & 7;
  289. audio_last_status[audio_owner] = ASC_PLAYING;
  290. audio_paused = false;
  291. g_audio_stopped = false;
  292. audio_start_dma();
  293. return true;
  294. }
  295. bool audio_set_paused(uint8_t id, bool paused)
  296. {
  297. if (audio_owner != (id & 7)) return false;
  298. else if (audio_paused && paused) return false;
  299. else if (!audio_paused && !paused) return false;
  300. if (paused)
  301. {
  302. // Turn off interrupts to stop flagging new audio samples to load
  303. dma_interrupt_disable(ODE_DMA, ODE_DMA_CH, DMA_INT_FTF | DMA_INT_HTF);
  304. dma_interrupt_flag_clear(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_HTF);
  305. dma_interrupt_flag_clear(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_FTF);
  306. // Set status for both left and right channel to stop audio polling
  307. // from loading new samples
  308. sbufst_a = READY;
  309. sbufst_b = READY;
  310. // Let the DMA continue to run but set audio out to 0s
  311. memset(sample_circ_buf, 0, AUDIO_BUFFER_SIZE);
  312. audio_last_status[audio_owner] = ASC_PAUSED;
  313. }
  314. else
  315. {
  316. // Enable audio polling and DMA interrupts to load and play new samples
  317. sbufst_a = STALE;
  318. sbufst_b = STALE;
  319. dma_interrupt_flag_clear(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_HTF);
  320. dma_interrupt_flag_clear(ODE_DMA, ODE_DMA_CH, DMA_INT_FLAG_FTF);
  321. dma_interrupt_enable(ODE_DMA, ODE_DMA_CH, DMA_INT_FTF | DMA_INT_HTF);
  322. audio_last_status[audio_owner] = ASC_PLAYING;
  323. }
  324. return true;
  325. }
  326. void audio_stop(uint8_t id)
  327. {
  328. if (audio_owner != (id & 7)) return;
  329. spi_disable(ODE_I2S_SPI);
  330. dma_channel_disable(ODE_DMA, ODE_DMA_CH);
  331. // idle the subsystem
  332. audio_last_status[audio_owner] = ASC_COMPLETED;
  333. audio_paused = false;
  334. g_audio_stopped = true;
  335. audio_owner = 0xFF;
  336. }
  337. audio_status_code audio_get_status_code(uint8_t id)
  338. {
  339. audio_status_code tmp = audio_last_status[id & 7];
  340. if (tmp == ASC_COMPLETED || tmp == ASC_ERRORED) {
  341. audio_last_status[id & 7] = ASC_NO_STATUS;
  342. }
  343. return tmp;
  344. }
  345. uint16_t audio_get_volume(uint8_t id)
  346. {
  347. return volumes[id & 7];
  348. }
  349. void audio_set_volume(uint8_t id, uint16_t vol)
  350. {
  351. volumes[id & 7] = vol;
  352. }
  353. uint16_t audio_get_channel(uint8_t id) {
  354. return channels[id & 7];
  355. }
  356. void audio_set_channel(uint8_t id, uint16_t chn) {
  357. channels[id & 7] = chn;
  358. }
  359. uint64_t audio_get_file_position()
  360. {
  361. return fpos;
  362. }
  363. void audio_set_file_position(uint8_t id, uint32_t lba)
  364. {
  365. fpos = 2352 * (uint64_t)lba;
  366. }
  367. #endif // ENABLE_AUDIO_OUTPUT_I2S