PageRenderTime 95ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/engines/fullpipe/sound.cpp

https://github.com/alexbevi/scummvm
C++ | 535 lines | 377 code | 136 blank | 22 comment | 93 complexity | c7944866891d2393acf5792860c3c0be MD5 | raw file
  1. /* ScummVM - Graphic Adventure Engine
  2. *
  3. * ScummVM is the legal property of its developers, whose names
  4. * are too numerous to list here. Please refer to the COPYRIGHT
  5. * file distributed with this source distribution.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. *
  21. */
  22. #include "fullpipe/fullpipe.h"
  23. #include "fullpipe/objects.h"
  24. #include "fullpipe/scene.h"
  25. #include "fullpipe/sound.h"
  26. #include "fullpipe/ngiarchive.h"
  27. #include "fullpipe/messages.h"
  28. #include "fullpipe/statics.h"
  29. #include "common/config-manager.h"
  30. #include "common/memstream.h"
  31. #include "audio/mixer.h"
  32. #include "audio/audiostream.h"
  33. #include "audio/decoders/vorbis.h"
  34. #include "audio/decoders/wave.h"
  35. namespace Fullpipe {
  36. bool SoundList::load(MfcArchive &file, const Common::String &fname) {
  37. debugC(5, kDebugLoading, "SoundList::load()");
  38. uint32 count = file.readUint32LE();
  39. _soundItems.resize(count);
  40. if (!fname.empty()) {
  41. _libHandle.reset(makeNGIArchive(fname));
  42. } else {
  43. _libHandle.reset();
  44. }
  45. for (uint i = 0; i < count; i++) {
  46. _soundItems[i].load(file, _libHandle.get());
  47. }
  48. return true;
  49. }
  50. bool SoundList::loadFile(const Common::String &fname, const Common::String &libname) {
  51. Common::File file;
  52. if (!file.open(fname))
  53. return false;
  54. MfcArchive archive(&file);
  55. return load(archive, libname);
  56. }
  57. Sound *SoundList::getSoundItemById(int id) {
  58. for (uint i = 0; i < _soundItems.size(); ++i) {
  59. if (_soundItems[i].getId() == id)
  60. return &_soundItems[i];
  61. }
  62. return nullptr;
  63. }
  64. Sound::Sound() :
  65. _id(0),
  66. _directSoundBuffer(0),
  67. _directSoundBuffers(),
  68. _soundData(nullptr),
  69. _handle(new Audio::SoundHandle()),
  70. _volume(100),
  71. _objectId(0) {}
  72. Sound::~Sound() {
  73. freeSound();
  74. delete _handle;
  75. }
  76. bool Sound::load(MfcArchive &file, NGIArchive *archive) {
  77. debugC(5, kDebugLoading, "Sound::load()");
  78. MemoryObject::load(file);
  79. _id = file.readUint32LE();
  80. /*_description = */file.readPascalString();
  81. assert(g_fp->_gameProjectVersion >= 6);
  82. _objectId = file.readUint16LE();
  83. if (archive && archive->hasFile(_memfilename)) {
  84. Common::SeekableReadStream *s = archive->createReadStreamForMember(_memfilename);
  85. _soundData = (byte *)calloc(s->size(), 1);
  86. s->read(_soundData, s->size());
  87. delete s;
  88. }
  89. return true;
  90. }
  91. void Sound::updateVolume() {
  92. // not needed in our implementation
  93. }
  94. void Sound::setPanAndVolumeByStaticAni() {
  95. if (!_objectId)
  96. return;
  97. StaticANIObject *ani = g_fp->_currentScene->getStaticANIObject1ById(_objectId, -1);
  98. if (!ani)
  99. return;
  100. int a, b;
  101. if (ani->_ox >= g_fp->_sceneRect.left) {
  102. int par, pan;
  103. if (ani->_ox <= g_fp->_sceneRect.right) {
  104. int dx;
  105. if (ani->_oy <= g_fp->_sceneRect.bottom) {
  106. if (ani->_oy >= g_fp->_sceneRect.top) {
  107. setPanAndVolume(g_fp->_sfxVolume, 0);
  108. return;
  109. }
  110. dx = g_fp->_sceneRect.top - ani->_oy;
  111. } else {
  112. dx = ani->_oy - g_fp->_sceneRect.bottom;
  113. }
  114. par = 0;
  115. if (dx > 800) {
  116. setPanAndVolume(-3500, 0);
  117. return;
  118. }
  119. pan = -3500;
  120. a = g_fp->_sfxVolume - (-3500);
  121. b = 800 - dx;
  122. } else {
  123. int dx = ani->_ox - g_fp->_sceneRect.right;
  124. if (dx > 800) {
  125. setPanAndVolume(-3500, 0);
  126. return;
  127. }
  128. pan = -3500;
  129. par = dx * (-3500) / -800;
  130. a = g_fp->_sfxVolume - (-3500);
  131. b = 800 - dx;
  132. }
  133. int32 pp = b * a;
  134. setPanAndVolume(pan + pp / 800, par);
  135. return;
  136. }
  137. int dx = g_fp->_sceneRect.left - ani->_ox;
  138. if (dx <= 800) {
  139. int32 s = (800 - dx) * (g_fp->_sfxVolume - (-3500));
  140. int32 p = -3500 + s / 800;
  141. if (p > g_fp->_sfxVolume)
  142. p = g_fp->_sfxVolume;
  143. setPanAndVolume(p, dx * (-3500) / 800);
  144. } else {
  145. setPanAndVolume(-3500, 0);
  146. }
  147. }
  148. void Sound::setPanAndVolume(int vol, int pan) {
  149. g_fp->_mixer->setChannelVolume(*_handle, MIN((vol + 10000) / 39, 255)); // -10000..0
  150. g_fp->_mixer->setChannelBalance(*_handle, CLIP(pan / 78, -127, 127)); // -10000..10000
  151. }
  152. void Sound::play(int flag) {
  153. Audio::SoundHandle *handle = getHandle();
  154. if (g_fp->_mixer->isSoundHandleActive(*handle)) { // We need to restart the music
  155. g_fp->_mixer->stopHandle(*handle);
  156. }
  157. byte *soundData = loadData();
  158. Common::MemoryReadStream *dataStream = new Common::MemoryReadStream(soundData, getDataSize());
  159. Audio::RewindableAudioStream *wav = Audio::makeWAVStream(dataStream, DisposeAfterUse::YES);
  160. Audio::AudioStream *audioStream = new Audio::LoopingAudioStream(wav, (flag == 1) ? 0 : 1);
  161. g_fp->_mixer->playStream(Audio::Mixer::kSFXSoundType, handle, audioStream);
  162. }
  163. void Sound::freeSound() {
  164. stop();
  165. free(_soundData);
  166. }
  167. int Sound::getVolume() {
  168. return g_fp->_mixer->getChannelVolume(*_handle) * 39; // 0..10000
  169. }
  170. void Sound::stop() {
  171. g_fp->_mixer->stopHandle(*_handle);
  172. }
  173. void FullpipeEngine::setSceneMusicParameters(GameVar *gvar) {
  174. stopSoundStream2();
  175. if (_mixer->isSoundHandleActive(_soundStream3))
  176. _mixer->stopHandle(_soundStream4);
  177. if (_musicLocal)
  178. stopAllSoundStreams();
  179. GameVar *var = gvar->getSubVarByName("MUSIC");
  180. for (int i = 0; i < 10; i++)
  181. _sceneTracks[i].clear();
  182. _numSceneTracks = 0;
  183. _sceneTrackHasSequence = false;
  184. if (!var)
  185. return;
  186. _musicGameVar = var;
  187. GameVar *tr = var->getSubVarByName("TRACKS");
  188. if (tr) {
  189. GameVar *sub = tr->_subVars;
  190. while (sub) {
  191. if (_musicAllowed & sub->_value.intValue) {
  192. _sceneTracks[_numSceneTracks] = sub->_varName;
  193. _numSceneTracks++;
  194. }
  195. sub = sub->_nextVarObj;
  196. }
  197. }
  198. _musicMinDelay = var->getSubVarAsInt("MINDELAY");
  199. _musicMaxDelay = var->getSubVarAsInt("MAXDELAY");
  200. _musicLocal = var->getSubVarAsInt("LOCAL");
  201. GameVar *seq = var->getSubVarByName("SEQUENCE");
  202. if (seq) {
  203. _sceneTrackHasSequence = true;
  204. _trackName = seq->_value.stringValue;
  205. }
  206. if (_musicLocal)
  207. stopAllSoundStreams();
  208. if (!_sceneTrackIsPlaying || _musicLocal)
  209. _trackStartDelay = var->getSubVarAsInt("STARTDELAY");
  210. }
  211. void FullpipeEngine::updateTrackDelay() {
  212. _sceneTrackIsPlaying = false;
  213. _trackStartDelay = _musicMinDelay + (_musicMaxDelay - _musicMinDelay) * (_updateTicks % 10) / 9;
  214. }
  215. void FullpipeEngine::startSceneTrack() {
  216. if (_sceneTrackIsPlaying) {
  217. if (!_mixer->isSoundHandleActive(_soundStream1)) { // Simulate end of sound callback
  218. updateTrackDelay();
  219. }
  220. }
  221. if (!_sceneTrackIsPlaying && _numSceneTracks > 0) {
  222. if (_trackStartDelay > 0) {
  223. _trackStartDelay--;
  224. } else {
  225. int trackNum = getSceneTrack();
  226. if (trackNum == -1) {
  227. _sceneTracksCurrentTrack = "silence";
  228. _trackStartDelay = 2880;
  229. _sceneTrackIsPlaying = 0;
  230. } else {
  231. _sceneTracksCurrentTrack = _sceneTracks[trackNum];
  232. startSoundStream1(_sceneTracksCurrentTrack);
  233. _sceneTrackIsPlaying = true;
  234. }
  235. }
  236. }
  237. }
  238. int FullpipeEngine::getSceneTrack() {
  239. int res;
  240. if (_sceneTrackHasSequence) {
  241. int num = _musicGameVar->getSubVarAsInt("TRACKS");
  242. if (_trackName[num + 1] == 's') { // 'silence'
  243. res = -1;
  244. } else {
  245. res = _trackName[num + 1] - '0';
  246. if (res < 0 || res >= _numSceneTracks)
  247. res = 0;
  248. }
  249. int track = num + 1;
  250. if (num + 2 >= (int)_trackName.size())
  251. track = 0;
  252. _musicGameVar->setSubVarAsInt("TRACKS", track);
  253. } else {
  254. res = _numSceneTracks * (_updateTicks % 10) / 10;
  255. }
  256. return res;
  257. }
  258. void FullpipeEngine::startSoundStream1(const Common::String &trackName) {
  259. stopAllSoundStreams();
  260. playOggSound(trackName, _soundStream1);
  261. }
  262. void FullpipeEngine::playOggSound(const Common::String &trackName, Audio::SoundHandle &stream) {
  263. #ifdef USE_VORBIS
  264. if (_mixer->isSoundHandleActive(stream))
  265. return;
  266. Common::File *track = new Common::File();
  267. if (!track->open(trackName)) {
  268. warning("Could not open %s", trackName.c_str());
  269. delete track;
  270. return;
  271. }
  272. Audio::RewindableAudioStream *ogg = Audio::makeVorbisStream(track, DisposeAfterUse::YES);
  273. _mixer->playStream(Audio::Mixer::kMusicSoundType, &stream, ogg);
  274. #endif
  275. }
  276. void FullpipeEngine::stopAllSounds() {
  277. for (int i = 0; i < _currSoundListCount; i++)
  278. for (int j = 0; j < _currSoundList1[i]->getCount(); j++) {
  279. _currSoundList1[i]->getSoundByIndex(j).stop();
  280. }
  281. }
  282. void FullpipeEngine::toggleMute() {
  283. if (_soundEnabled) {
  284. _sfxVolume = _sfxVolume != -10000 ? -10000 : 0;
  285. updateSoundVolume();
  286. }
  287. }
  288. void FullpipeEngine::playSound(int id, int flag) {
  289. Sound *sound = 0;
  290. for (int i = 0; i < _currSoundListCount; i++) {
  291. sound = _currSoundList1[i]->getSoundItemById(id);
  292. if (sound)
  293. break;
  294. }
  295. if (!sound) {
  296. warning("playSound: Can't find sound with ID %d", id);
  297. return;
  298. }
  299. sound->play(flag);
  300. }
  301. void FullpipeEngine::playTrack(GameVar *sceneVar, const char *name, bool delayed) {
  302. if (_mixer->isSoundHandleActive(_soundStream3))
  303. _mixer->stopHandle(_soundStream4);
  304. stopSoundStream2();
  305. if (_musicLocal)
  306. stopAllSoundStreams();
  307. GameVar *var = sceneVar->getSubVarByName(name);
  308. for (int i = 0; i < 10; i++)
  309. _sceneTracks[i].clear();
  310. _numSceneTracks = 0;
  311. _sceneTrackHasSequence = false;
  312. if (!var)
  313. return;
  314. _musicGameVar = var;
  315. GameVar *tr = var->getSubVarByName("TRACKS");
  316. if (tr) {
  317. GameVar *sub = tr->_subVars;
  318. while (sub) {
  319. if (_musicAllowed & sub->_value.intValue) {
  320. _sceneTracks[_numSceneTracks] = sub->_varName;
  321. _numSceneTracks++;
  322. }
  323. sub = sub->_nextVarObj;
  324. }
  325. }
  326. _musicMinDelay = var->getSubVarAsInt("MINDELAY");
  327. _musicMaxDelay = var->getSubVarAsInt("MAXDELAY");
  328. _musicLocal = var->getSubVarAsInt("LOCAL");
  329. GameVar *seq = var->getSubVarByName("SEQUENCE");
  330. if (seq) {
  331. _sceneTrackHasSequence = true;
  332. _trackName = seq->_value.stringValue;
  333. }
  334. if (delayed) {
  335. if (_sceneTrackIsPlaying && _numSceneTracks == 1) {
  336. if (_sceneTracksCurrentTrack != _sceneTracks[0])
  337. stopAllSoundStreams();
  338. }
  339. _trackStartDelay = var->getSubVarAsInt("STARTDELAY");
  340. }
  341. }
  342. void global_messageHandler_handleSound(ExCommand *cmd) {
  343. if (!g_fp->_soundEnabled)
  344. return;
  345. Sound *snd = 0;
  346. for (int i = 0; i < g_fp->_currSoundListCount; i++)
  347. if ((snd = g_fp->_currSoundList1[i]->getSoundItemById(cmd->_messageNum)) != NULL)
  348. break;
  349. if (!snd)
  350. return;
  351. if (cmd->_z & 1) {
  352. if (!g_fp->_flgSoundList && (cmd->_z & 4))
  353. snd->freeSound();
  354. snd->updateVolume();
  355. if (snd->_objectId && g_fp->_currentScene->getStaticANIObject1ById(snd->_objectId, -1))
  356. snd->setPanAndVolumeByStaticAni();
  357. else
  358. snd->setPanAndVolume(g_fp->_sfxVolume, 0);
  359. if (snd->getVolume() > -3500)
  360. snd->play(cmd->_param);
  361. } else if (cmd->_z & 2) {
  362. snd->stop();
  363. }
  364. }
  365. void FullpipeEngine::stopSoundStream2() {
  366. _stream2playing = false;
  367. if (_mixer->isSoundHandleActive(_soundStream3)) {
  368. _mixer->stopHandle(_soundStream2);
  369. _mixer->stopHandle(_soundStream3);
  370. }
  371. }
  372. void FullpipeEngine::stopAllSoundStreams() {
  373. _mixer->stopHandle(_soundStream1);
  374. _mixer->stopHandle(_soundStream2);
  375. _mixer->stopHandle(_soundStream3);
  376. _mixer->stopHandle(_soundStream4);
  377. _stream2playing = false;
  378. }
  379. void FullpipeEngine::stopAllSoundInstances(int id) {
  380. for (int i = 0; i < _currSoundListCount; i++) {
  381. Sound *sound = _currSoundList1[i]->getSoundItemById(id);
  382. if (sound)
  383. sound->stop();
  384. }
  385. }
  386. void FullpipeEngine::updateSoundVolume() {
  387. ConfMan.setInt("sfx_volume", MAX((_sfxVolume + 10000) / 39, 255));
  388. syncSoundSettings();
  389. for (int i = 0; i < _currSoundListCount; i++)
  390. for (int j = 0; j < _currSoundList1[i]->getCount(); j++) {
  391. _currSoundList1[i]->getSoundByIndex(j).setPanAndVolume(_sfxVolume, 0);
  392. }
  393. }
  394. void FullpipeEngine::setMusicVolume(int vol) {
  395. _musicVolume = vol;
  396. ConfMan.setInt("music_volume", _musicVolume);
  397. syncSoundSettings();
  398. }
  399. } // End of namespace Fullpipe