MusicMP3.cpp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. #include <iostream>
  2. #include <cpp3ds/System/I18n.hpp>
  3. #include <cpp3ds/System/FileInputStream.hpp>
  4. #include <cpp3ds/System/Sleep.hpp>
  5. #include <arpa/inet.h>
  6. #include <netinet/in.h>
  7. #include <cpp3ds/Window.hpp>
  8. #include "MusicMP3.hpp"
  9. #include "Notification.hpp"
  10. //Thanks to MaK11-12's ctrmus
  11. #define CHANNEL 0x08
  12. #ifndef EMULATION
  13. //MPG123 stuff
  14. size_t* m_buffSize = NULL;
  15. mpg123_handle *m_mh = NULL;
  16. int16_t* m_buffer1 = NULL;
  17. int16_t* m_buffer2 = NULL;
  18. #endif
  19. namespace FreeShop {
  20. MusicMP3::MusicMP3()
  21. : m_thread(&MusicMP3::streamData, this)
  22. , m_isStreaming (false)
  23. , m_isPaused (false)
  24. , m_isStopped (false)
  25. , m_isSongFinished(false)
  26. , m_channelCount (0)
  27. {
  28. m_thread.setPriority(0x1A);
  29. }
  30. MusicMP3::~MusicMP3()
  31. {
  32. stop();
  33. }
  34. bool MusicMP3::openFromFile(const std::string &filename)
  35. {
  36. if (!cpp3ds::Service::isEnabled(cpp3ds::Audio))
  37. return false;
  38. stop();
  39. if (!m_file.open(filename))
  40. return false;
  41. m_isLittleEndian = true; // Default to true so initial reads are not swapped
  42. //Verify header magic value
  43. cpp3ds::Uint32 magic;
  44. m_file.seek(0);
  45. m_file.read(&magic, 4);
  46. if (magic != 0x04334449 && magic != 0x6490FBFF)
  47. return false;
  48. m_fileName = filename;
  49. #ifndef EMULATION
  50. //Var inits
  51. int err = 0;
  52. int encoding = 0;
  53. m_buffSize = static_cast<size_t*>(linearAlloc(sizeof(size_t)));
  54. *m_buffSize = 0;
  55. //Disable paused and stopped flags
  56. m_isPaused = false;
  57. m_isStopped = false;
  58. //Init the mpg123
  59. if(mpg123_init() != MPG123_OK)
  60. return false;
  61. //Get the mpg123's handle
  62. if((m_mh = mpg123_new(NULL, &err)) == NULL)
  63. {
  64. printf("Error: %s\n", mpg123_plain_strerror(err));
  65. return false;
  66. }
  67. //Open the file and get informations from it
  68. if(mpg123_open(m_mh, filename.c_str()) != MPG123_OK ||
  69. mpg123_getformat(m_mh, (long *) &m_sampleRate, (int *) &m_channelCount, &encoding) != MPG123_OK)
  70. {
  71. printf("Trouble with mpg123: %s\n", mpg123_strerror(m_mh));
  72. return false;
  73. }
  74. /*
  75. * Ensure that this output format will not change (it might, when we allow
  76. * it).
  77. */
  78. mpg123_format_none(m_mh);
  79. mpg123_format(m_mh, m_sampleRate, m_channelCount, encoding);
  80. /*
  81. * Buffer could be almost any size here, mpg123_outblock() is just some
  82. * recommendation. The size should be a multiple of the PCM frame size.
  83. */
  84. *m_buffSize = mpg123_outblock(m_mh) * 16;
  85. #endif
  86. return true;
  87. }
  88. void MusicMP3::play(bool launchThread)
  89. {
  90. if (m_isPaused)
  91. {
  92. #ifdef _3DS
  93. for (int i = 0; i < m_channelCount; ++i)
  94. ndspChnSetPaused(m_channel[i], false);
  95. #endif
  96. m_isPaused = false;
  97. return;
  98. }
  99. if (m_isStreaming)
  100. stop();
  101. #ifdef _3DS
  102. size_t buffSize = *m_buffSize;
  103. m_buffer1 = static_cast<int16_t*>(linearAlloc(buffSize * sizeof(int16_t)));
  104. m_buffer2 = static_cast<int16_t*>(linearAlloc(buffSize * sizeof(int16_t)));
  105. ndspChnReset(CHANNEL);
  106. ndspChnWaveBufClear(CHANNEL);
  107. ndspSetOutputMode(NDSP_OUTPUT_STEREO);
  108. ndspChnSetInterp(CHANNEL, NDSP_INTERP_POLYPHASE);
  109. ndspChnSetRate(CHANNEL, m_sampleRate);
  110. ndspChnSetFormat(CHANNEL,
  111. m_channelCount == 2 ? NDSP_FORMAT_STEREO_PCM16 :
  112. NDSP_FORMAT_MONO_PCM16);
  113. memset(m_waveBuf, 0, sizeof(m_waveBuf));
  114. m_waveBuf[0].nsamples = decodeMP3(&m_buffer1[0]) / m_channelCount;
  115. m_waveBuf[0].data_vaddr = &m_buffer1[0];
  116. ndspChnWaveBufAdd(CHANNEL, &m_waveBuf[0]);
  117. m_waveBuf[1].nsamples = decodeMP3(&m_buffer2[0]) / m_channelCount;
  118. m_waveBuf[1].data_vaddr = &m_buffer2[0];
  119. ndspChnWaveBufAdd(CHANNEL, &m_waveBuf[1]);
  120. #endif
  121. m_isStreaming = true;
  122. m_isSongFinished = false;
  123. if (launchThread)
  124. m_thread.launch();
  125. }
  126. void MusicMP3::pause()
  127. {
  128. cpp3ds::Lock lock(m_mutex);
  129. if (!m_isStreaming)
  130. return;
  131. m_isPaused = true;
  132. #ifdef _3DS
  133. for (int i = 0; i < m_channelCount; ++i)
  134. ndspChnSetPaused(m_channel[i], true);
  135. #endif
  136. }
  137. void MusicMP3::stop()
  138. {
  139. if (!m_isStreaming)
  140. return;
  141. {
  142. cpp3ds::Lock lock(m_mutex);
  143. m_isStreaming = false;
  144. m_isStopped = true;
  145. }
  146. m_thread.wait();
  147. #ifdef _3DS
  148. cpp3ds::Lock lock(cpp3ds::g_activeNdspChannelsMutex);
  149. ndspChnWaveBufClear(CHANNEL);
  150. linearFree(m_buffer1);
  151. linearFree(m_buffer2);
  152. mpg123_close(m_mh);
  153. mpg123_delete(m_mh);
  154. mpg123_exit();
  155. #endif
  156. }
  157. void MusicMP3::streamData()
  158. {
  159. bool isPaused = false;
  160. while (1)
  161. {
  162. {
  163. cpp3ds::Lock lock(m_mutex);
  164. isPaused = m_isPaused;
  165. if (!m_isStreaming)
  166. break;
  167. }
  168. if (!isPaused)
  169. {
  170. fillBuffers();
  171. }
  172. m_isSongFinished = true;
  173. cpp3ds::Lock lock(m_mutex);
  174. m_isStreaming = false;
  175. if (!m_isStopped) {
  176. #ifdef _3DS
  177. ndspChnWaveBufClear(CHANNEL);
  178. linearFree(m_buffer1);
  179. linearFree(m_buffer2);
  180. mpg123_close(m_mh);
  181. mpg123_delete(m_mh);
  182. mpg123_exit();
  183. #endif
  184. openFromFile(m_fileName);
  185. play(false);
  186. } else {
  187. break;
  188. }
  189. cpp3ds::sleep(cpp3ds::milliseconds(100));
  190. }
  191. }
  192. void MusicMP3::fillBuffers()
  193. {
  194. #ifndef EMULATION
  195. bool lastbuf = false;
  196. bool songFinished = false;
  197. m_isSongFinished = false;
  198. while(!m_isStopped && !songFinished && m_isStreaming)
  199. {
  200. svcSleepThread(100 * 1000);
  201. /* When the last buffer has finished playing, break. */
  202. if(lastbuf == true && m_waveBuf[0].status == NDSP_WBUF_DONE && m_waveBuf[1].status == NDSP_WBUF_DONE) {
  203. songFinished = true;
  204. break;
  205. }
  206. if(ndspChnIsPaused(CHANNEL) == true || lastbuf == true)
  207. continue;
  208. if(m_waveBuf[0].status == NDSP_WBUF_DONE)
  209. {
  210. size_t read = decodeMP3(&m_buffer1[0]);
  211. if(read <= 0)
  212. {
  213. lastbuf = true;
  214. continue;
  215. }
  216. else if(read < *m_buffSize)
  217. m_waveBuf[0].nsamples = read / m_channelCount;
  218. ndspChnWaveBufAdd(CHANNEL, &m_waveBuf[0]);
  219. }
  220. if(m_waveBuf[1].status == NDSP_WBUF_DONE)
  221. {
  222. size_t read = decodeMP3(&m_buffer2[0]);
  223. if(read <= 0)
  224. {
  225. lastbuf = true;
  226. continue;
  227. }
  228. else if(read < *m_buffSize)
  229. m_waveBuf[1].nsamples = read / m_channelCount;
  230. ndspChnWaveBufAdd(CHANNEL, &m_waveBuf[1]);
  231. }
  232. DSP_FlushDataCache(m_buffer1, *m_buffSize * sizeof(int16_t));
  233. DSP_FlushDataCache(m_buffer2, *m_buffSize * sizeof(int16_t));
  234. }
  235. #endif
  236. }
  237. #ifndef EMULATION
  238. uint64_t MusicMP3::decodeMP3(void* buffer)
  239. {
  240. size_t done = 0;
  241. mpg123_read(m_mh, static_cast<unsigned char*>(buffer), *m_buffSize, &done);
  242. return done / (sizeof(int16_t));
  243. }
  244. #endif
  245. bool MusicMP3::getSongFinished()
  246. {
  247. return m_isSongFinished;
  248. }
  249. } // namespace FreeShop