AudioEngine-linux.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /**
  2. * @author cesarpachon
  3. */
  4. #include <cstring>
  5. #include <cstdint>
  6. #include "audio/linux/AudioEngine-linux.h"
  7. #include "base/CCDirector.h"
  8. #include "base/CCScheduler.h"
  9. #include "platform/CCFileUtils.h"
  10. using namespace cocos2d;
  11. using namespace cocos2d::experimental;
  12. AudioEngineImpl * g_AudioEngineImpl = nullptr;
  13. void ERRCHECKWITHEXIT(FMOD_RESULT result)
  14. {
  15. if (result != FMOD_OK) {
  16. printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
  17. }
  18. }
  19. bool ERRCHECK(FMOD_RESULT result)
  20. {
  21. if (result != FMOD_OK) {
  22. printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
  23. return true;
  24. }
  25. return false;
  26. }
  27. FMOD_RESULT F_CALLBACK channelCallback(FMOD_CHANNELCONTROL *channelcontrol,
  28. FMOD_CHANNELCONTROL_TYPE controltype,
  29. FMOD_CHANNELCONTROL_CALLBACK_TYPE callbacktype,
  30. void *commandData1, void *commandData2)
  31. {
  32. if (controltype == FMOD_CHANNELCONTROL_CHANNEL && callbacktype == FMOD_CHANNELCONTROL_CALLBACK_END) {
  33. g_AudioEngineImpl->onSoundFinished((FMOD::Channel *)channelcontrol);
  34. }
  35. return FMOD_OK;
  36. }
  37. AudioEngineImpl::AudioEngineImpl()
  38. {
  39. }
  40. AudioEngineImpl::~AudioEngineImpl()
  41. {
  42. FMOD_RESULT result;
  43. result = pSystem->close();
  44. ERRCHECKWITHEXIT(result);
  45. result = pSystem->release();
  46. ERRCHECKWITHEXIT(result);
  47. }
  48. bool AudioEngineImpl::init()
  49. {
  50. FMOD_RESULT result;
  51. /*
  52. Create a System object and initialize.
  53. */
  54. result = FMOD::System_Create(&pSystem);
  55. ERRCHECKWITHEXIT(result);
  56. result = pSystem->setOutput(FMOD_OUTPUTTYPE_AUTODETECT);
  57. ERRCHECKWITHEXIT(result);
  58. result = pSystem->init(32, FMOD_INIT_NORMAL, 0);
  59. ERRCHECKWITHEXIT(result);
  60. mapChannelInfo.clear();
  61. mapSound.clear();
  62. auto scheduler = cocos2d::Director::getInstance()->getScheduler();
  63. scheduler->schedule(schedule_selector(AudioEngineImpl::update), this, 0.05f, false);
  64. g_AudioEngineImpl = this;
  65. return true;
  66. }
  67. int AudioEngineImpl::play2d(const std::string &fileFullPath, bool loop, float volume)
  68. {
  69. int id = preload(fileFullPath, nullptr);
  70. if (id >= 0) {
  71. mapChannelInfo[id].loop=loop;
  72. mapChannelInfo[id].channel->setPaused(true);
  73. mapChannelInfo[id].volume = volume;
  74. AudioEngine::_audioIDInfoMap[id].state = AudioEngine::AudioState::PAUSED;
  75. resume(id);
  76. }
  77. return id;
  78. }
  79. void AudioEngineImpl::setVolume(int audioID, float volume)
  80. {
  81. try {
  82. mapChannelInfo[audioID].channel->setVolume(volume);
  83. }
  84. catch (const std::out_of_range& oor) {
  85. printf("AudioEngineImpl::setVolume: invalid audioID: %d\n", audioID);
  86. }
  87. }
  88. void AudioEngineImpl::setLoop(int audioID, bool loop)
  89. {
  90. try {
  91. mapChannelInfo[audioID].channel->setLoopCount(loop ? -1 : 0);
  92. }
  93. catch (const std::out_of_range& oor) {
  94. printf("AudioEngineImpl::setLoop: invalid audioID: %d\n", audioID);
  95. }
  96. }
  97. bool AudioEngineImpl::pause(int audioID)
  98. {
  99. try {
  100. mapChannelInfo[audioID].channel->setPaused(true);
  101. AudioEngine::_audioIDInfoMap[audioID].state = AudioEngine::AudioState::PAUSED;
  102. return true;
  103. }
  104. catch (const std::out_of_range& oor) {
  105. printf("AudioEngineImpl::pause: invalid audioID: %d\n", audioID);
  106. return false;
  107. }
  108. }
  109. bool AudioEngineImpl::resume(int audioID)
  110. {
  111. try {
  112. if (!mapChannelInfo[audioID].channel) {
  113. FMOD::Channel *channel = nullptr;
  114. FMOD::ChannelGroup *channelgroup = nullptr;
  115. //starts the sound in pause mode, use the channel to unpause
  116. FMOD_RESULT result = pSystem->playSound(mapChannelInfo[audioID].sound, channelgroup, true, &channel);
  117. if (ERRCHECK(result)) {
  118. return false;
  119. }
  120. channel->setMode(mapChannelInfo[audioID].loop ? FMOD_LOOP_NORMAL : FMOD_LOOP_OFF);
  121. channel->setLoopCount(mapChannelInfo[audioID].loop ? -1 : 0);
  122. channel->setVolume(mapChannelInfo[audioID].volume);
  123. channel->setUserData(reinterpret_cast<void *>(static_cast<std::intptr_t>(mapChannelInfo[audioID].id)));
  124. mapChannelInfo[audioID].channel = channel;
  125. }
  126. mapChannelInfo[audioID].channel->setPaused(false);
  127. AudioEngine::_audioIDInfoMap[audioID].state = AudioEngine::AudioState::PLAYING;
  128. return true;
  129. }
  130. catch (const std::out_of_range& oor) {
  131. printf("AudioEngineImpl::resume: invalid audioID: %d\n", audioID);
  132. return false;
  133. }
  134. }
  135. bool AudioEngineImpl::stop(int audioID)
  136. {
  137. try {
  138. mapChannelInfo[audioID].channel->stop();
  139. mapChannelInfo[audioID].channel = nullptr;
  140. return true;
  141. }
  142. catch (const std::out_of_range& oor) {
  143. printf("AudioEngineImpl::stop: invalid audioID: %d\n", audioID);
  144. return false;
  145. }
  146. }
  147. void AudioEngineImpl::stopAll()
  148. {
  149. for (auto& it : mapChannelInfo) {
  150. ChannelInfo & audioRef = it.second;
  151. audioRef.channel->stop();
  152. audioRef.channel = nullptr;
  153. }
  154. }
  155. float AudioEngineImpl::getDuration(int audioID)
  156. {
  157. try {
  158. FMOD::Sound * sound = mapChannelInfo[audioID].sound;
  159. unsigned int length;
  160. FMOD_RESULT result = sound->getLength(&length, FMOD_TIMEUNIT_MS);
  161. ERRCHECK(result);
  162. float duration = (float)length / 1000.0f;
  163. return duration;
  164. }
  165. catch (const std::out_of_range& oor) {
  166. printf("AudioEngineImpl::getDuration: invalid audioID: %d\n", audioID);
  167. return AudioEngine::TIME_UNKNOWN;
  168. }
  169. }
  170. float AudioEngineImpl::getCurrentTime(int audioID)
  171. {
  172. try {
  173. unsigned int position;
  174. FMOD_RESULT result = mapChannelInfo[audioID].channel->getPosition(&position, FMOD_TIMEUNIT_MS);
  175. ERRCHECK(result);
  176. float currenttime = position /1000.0f;
  177. return currenttime;
  178. }
  179. catch (const std::out_of_range& oor) {
  180. printf("AudioEngineImpl::getCurrentTime: invalid audioID: %d\n", audioID);
  181. return AudioEngine::TIME_UNKNOWN;
  182. }
  183. }
  184. bool AudioEngineImpl::setCurrentTime(int audioID, float time)
  185. {
  186. bool ret = false;
  187. try {
  188. unsigned int position = (unsigned int)(time * 1000.0f);
  189. FMOD_RESULT result = mapChannelInfo[audioID].channel->setPosition(position, FMOD_TIMEUNIT_MS);
  190. ret = !ERRCHECK(result);
  191. }
  192. catch (const std::out_of_range& oor) {
  193. printf("AudioEngineImpl::setCurrentTime: invalid audioID: %d\n", audioID);
  194. }
  195. return ret;
  196. }
  197. void AudioEngineImpl::setFinishCallback(int audioID, const std::function<void (int, const std::string &)> &callback)
  198. {
  199. try {
  200. FMOD::Channel * channel = mapChannelInfo[audioID].channel;
  201. mapChannelInfo[audioID].callback = callback;
  202. FMOD_RESULT result = channel->setCallback(channelCallback);
  203. ERRCHECK(result);
  204. }
  205. catch (const std::out_of_range& oor) {
  206. printf("AudioEngineImpl::setFinishCallback: invalid audioID: %d\n", audioID);
  207. }
  208. }
  209. void AudioEngineImpl::onSoundFinished(FMOD::Channel * channel)
  210. {
  211. int id = 0;
  212. try {
  213. void * data;
  214. channel->getUserData(&data);
  215. id = static_cast<int>(reinterpret_cast<std::intptr_t>(data));
  216. if (mapChannelInfo[id].callback) {
  217. mapChannelInfo[id].callback(id, mapChannelInfo[id].path);
  218. }
  219. mapChannelInfo[id].channel = nullptr;
  220. }
  221. catch (const std::out_of_range& oor) {
  222. printf("AudioEngineImpl::onSoundFinished: invalid audioID: %d\n", id);
  223. }
  224. }
  225. void AudioEngineImpl::uncache(const std::string& path)
  226. {
  227. std::string fullPath = FileUtils::getInstance()->fullPathForFilename(path);
  228. std::map<std::string, FMOD::Sound *>::const_iterator it = mapSound.find(fullPath);
  229. if (it!=mapSound.end()) {
  230. FMOD::Sound * sound = it->second;
  231. if (sound) {
  232. sound->release();
  233. }
  234. mapSound.erase(it);
  235. }
  236. if (mapId.find(path) != mapId.end())
  237. mapId.erase(path);
  238. }
  239. void AudioEngineImpl::uncacheAll()
  240. {
  241. for (const auto& it : mapSound) {
  242. auto sound = it.second;
  243. if (sound) {
  244. sound->release();
  245. }
  246. }
  247. mapSound.clear();
  248. mapId.clear();
  249. }
  250. int AudioEngineImpl::preload(const std::string& filePath, std::function<void(bool isSuccess)> callback)
  251. {
  252. FMOD::Sound * sound = findSound(filePath);
  253. if (!sound) {
  254. std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filePath);
  255. FMOD_RESULT result = pSystem->createSound(fullPath.c_str(), FMOD_LOOP_OFF, 0, &sound);
  256. if (ERRCHECK(result)) {
  257. printf("sound effect in %s could not be preload\n", filePath.c_str());
  258. if (callback) {
  259. callback(false);
  260. }
  261. return -1;
  262. }
  263. mapSound[fullPath] = sound;
  264. }
  265. int id = static_cast<int>(mapChannelInfo.size()) + 1;
  266. if (mapId.find(filePath) == mapId.end())
  267. mapId.insert({filePath, id});
  268. else
  269. id = mapId.at(filePath);
  270. auto& chanelInfo = mapChannelInfo[id];
  271. chanelInfo.sound = sound;
  272. chanelInfo.id = id;
  273. chanelInfo.channel = nullptr;
  274. chanelInfo.callback = nullptr;
  275. chanelInfo.path = filePath;
  276. //we are going to use UserData to store pointer to Channel when playing
  277. chanelInfo.sound->setUserData(reinterpret_cast<void *>(static_cast<std::intptr_t>(id)));
  278. if (callback) {
  279. callback(true);
  280. }
  281. return id;
  282. }
  283. void AudioEngineImpl::update(float dt)
  284. {
  285. pSystem->update();
  286. }
  287. FMOD::Sound * AudioEngineImpl::findSound(const std::string &path)
  288. {
  289. std::string fullPath = FileUtils::getInstance()->fullPathForFilename(path);
  290. std::map<std::string, FMOD::Sound *>::const_iterator it = mapSound.find(fullPath);
  291. return (it != mapSound.end()) ? (it->second) : nullptr;
  292. }
  293. FMOD::Channel * AudioEngineImpl::getChannel(FMOD::Sound *sound)
  294. {
  295. void * data;
  296. sound->getUserData(&data);
  297. int id = static_cast<int>(reinterpret_cast<std::intptr_t>(data));
  298. return mapChannelInfo[id].channel;
  299. }