/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "titanic/sound/music_room_instrument.h" #include "titanic/sound/sound_manager.h" #include "titanic/core/project_item.h" #include "titanic/core/game_object.h" namespace Titanic { bool CMusicRoomInstrument::_pianoToggle; int CMusicRoomInstrument::_pianoCtr; int CMusicRoomInstrument::_bassCtr; byte *CMusicRoomInstrument::_buffer; double *CMusicRoomInstrument::_array; int CMusicRoomInstrument::_arrayIndex; void CMusicRoomInstrument::init() { _pianoToggle = false; _pianoCtr = 0; _bassCtr = 0; _buffer = nullptr; _array = nullptr; _arrayIndex = 0; } void CMusicRoomInstrument::deinit() { delete[] _buffer; delete[] _array; _buffer = nullptr; } CMusicRoomInstrument::CMusicRoomInstrument(CProjectItem *project, CSoundManager *soundManager, MusicWaveInstrument instrument) : _project(project), _soundManager(soundManager), _instrument(instrument) { Common::fill(&_gameObjects[0], &_gameObjects[4], (CGameObject *)nullptr); _animTime = 0.0; _waveIndex = -1; _readPos = 0; _readIncrement = 0; _size = 0; _count = 0; _field4C = 0; switch (instrument) { case MV_PIANO: _gameObjects[0] = static_cast(_project->findByName("Piano Man")); _gameObjects[1] = static_cast(_project->findByName("Piano Mouth")); _gameObjects[2] = static_cast(_project->findByName("Piano Left Arm")); _gameObjects[3] = static_cast(_project->findByName("Piano Right Arm")); _animTime = 0.45; break; case MV_BASS: _gameObjects[0] = static_cast(_project->findByName("Bass Player")); break; case MV_BELLS: _gameObjects[0] = static_cast(_project->findByName("Tubular Bells")); _animTime = 0.4; break; case MV_SNAKE: _gameObjects[0] = static_cast(_project->findByName("Snake_Hammer")); _gameObjects[1] = static_cast(_project->findByName("Snake_Glass")); _gameObjects[2] = static_cast(_project->findByName("Snake_Head")); _animTime = 0.17; break; } } void CMusicRoomInstrument::setFilesCount(uint count) { assert(_items.empty()); _items.resize(count); } void CMusicRoomInstrument::load(int index, const CString &filename, int v3) { assert(!_items[index]._waveFile); _items[index]._waveFile = createWaveFile(filename); _items[index]._value = v3; } CWaveFile *CMusicRoomInstrument::createWaveFile(const CString &name) { if (name.empty()) return nullptr; return _soundManager->loadSound(name); } void CMusicRoomInstrument::start() { if (_gameObjects[0]) { switch (_instrument) { case MV_PIANO: _gameObjects[0]->playMovie(0, 29, MOVIE_STOP_PREVIOUS); _gameObjects[2]->loadFrame(14); _gameObjects[3]->loadFrame(22); break; case MV_BELLS: _gameObjects[0]->loadFrame(0); _gameObjects[0]->movieSetPlaying(true); break; case MV_SNAKE: _field4C = 22; _gameObjects[1]->playMovie(0, 22, 0); _gameObjects[2]->playMovie(0, 35, MOVIE_STOP_PREVIOUS); _gameObjects[0]->playMovie(0, 1, MOVIE_STOP_PREVIOUS); _gameObjects[0]->playMovie(0, 1, 0); _gameObjects[0]->playMovie(0, 1, 0); _gameObjects[0]->playMovie(0, 1, 0); _gameObjects[0]->playMovie(0, 1, 0); break; default: break; } } } void CMusicRoomInstrument::stop() { if (_gameObjects[0]) { switch (_instrument) { case MV_PIANO: _gameObjects[1]->setVisible(false); _gameObjects[2]->setVisible(false); _gameObjects[3]->setVisible(false); _gameObjects[0]->playMovie(29, 58, MOVIE_STOP_PREVIOUS); break; case MV_BELLS: _gameObjects[0]->stopMovie(); break; default: break; } } } void CMusicRoomInstrument::update(int val) { if (_gameObjects[0]) { switch (_instrument) { case MV_PIANO: _gameObjects[1]->setVisible(true); _gameObjects[2]->setVisible(true); _gameObjects[3]->setVisible(true); _gameObjects[_pianoToggle ? 3 : 2]->playMovie(MOVIE_STOP_PREVIOUS); _pianoToggle = !_pianoToggle; switch (_pianoCtr) { case 0: _gameObjects[1]->playMovie(0, 4, MOVIE_STOP_PREVIOUS); break; case 1: _gameObjects[1]->playMovie(4, 8, MOVIE_STOP_PREVIOUS); break; case 2: _gameObjects[1]->playMovie(8, 12, MOVIE_STOP_PREVIOUS); break; case 3: _gameObjects[1]->playMovie(12, 16, MOVIE_STOP_PREVIOUS); break; default: break; } _pianoCtr = (_pianoCtr + 1) % 4; break; case MV_BASS: switch (_bassCtr) { case 0: _gameObjects[0]->playMovie(0, 7, MOVIE_STOP_PREVIOUS); break; case 1: _gameObjects[0]->playMovie(7, 14, MOVIE_STOP_PREVIOUS); break; case 2: _gameObjects[0]->playMovie(15, 24, MOVIE_STOP_PREVIOUS); break; case 3: _gameObjects[0]->playMovie(25, 33, MOVIE_STOP_PREVIOUS); break; default: break; } // WORKAROUND: Original didn't change the selected bass animation _bassCtr = (_bassCtr + 1) % 4; break; case MV_BELLS: switch (val) { case 60: _gameObjects[0]->playMovie(0, 512, MOVIE_STOP_PREVIOUS); _gameObjects[0]->movieSetPlaying(true); _animTime = 0.6; break; case 62: _gameObjects[0]->playMovie(828, 1023, MOVIE_STOP_PREVIOUS); _animTime = 0.3; break; case 63: _gameObjects[0]->playMovie(1024, 1085, MOVIE_STOP_PREVIOUS); break; default: break; } break; case MV_SNAKE: { _gameObjects[0]->playMovie(0, 7, MOVIE_STOP_PREVIOUS); double tempVal = 46.0 - ((double)(val - 14) * 1.43); int frameNum = _field4C; int frameNum1 = (int)((tempVal - frameNum) * 0.25); _gameObjects[1]->playMovie(frameNum1, frameNum1, MOVIE_STOP_PREVIOUS); frameNum += frameNum1; _gameObjects[1]->playMovie(frameNum, frameNum, 0); frameNum += frameNum1; _gameObjects[1]->playMovie(frameNum, frameNum, 0); _gameObjects[2]->playMovie(45, 49, MOVIE_STOP_PREVIOUS); break; } default: break; } } } void CMusicRoomInstrument::clear() { _waveIndex = 0; _readPos = 0; _readIncrement = 0; _size = 0; _count = 0; } void CMusicRoomInstrument::reset(uint total) { _waveIndex = -1; _readPos = 0; _readIncrement = 0; _size = total; _count = 0; } int CMusicRoomInstrument::read(int16 *ptr, uint size) { if (!_size) return 0; if (size >= _size) size = _size; if (_waveIndex != -1) { // Lock the specified wave file for access const int16 *data = _items[_waveIndex]._waveFile->lock(); assert(data); const int16 *src = data; // Loop through merging data from the wave file into the dest buffer for (uint idx = 0; idx < (size / sizeof(int16)); ++idx, _readPos += _readIncrement) { uint srcPos = _readPos >> 8; if (srcPos >= _count) break; int16 val = READ_LE_UINT16(src + srcPos); *ptr++ += val; } // Unlock the wave file _items[_waveIndex]._waveFile->unlock(data); } _size -= size; return size; } void CMusicRoomInstrument::chooseWaveFile(int index, int size) { if (!_array) setupArray(-36, 36); int minDiff = ABS(_items[0]._value - index); int waveIndex = 0; for (uint idx = 1; idx < _items.size(); ++idx) { int diff = ABS(_items[idx]._value - index); if (diff < minDiff) { minDiff = diff; waveIndex = idx; } } const CInstrumentWaveFile &wf = _items[waveIndex]; int arrIndex = _arrayIndex - wf._value + index; uint waveSize = wf._waveFile->size(); _waveIndex = waveIndex; _readPos = 0; _readIncrement = (int)(_array[arrIndex] * 256); _size = size; _count = waveSize / 2; } void CMusicRoomInstrument::setupArray(int minVal, int maxVal) { // Delete any prior array and recreate it delete[] _array; int arrSize = maxVal - minVal + 1; _array = new double[arrSize]; _arrayIndex = ABS(minVal); // Setup array contents _array[_arrayIndex] = 1.0; double val = 1.0594634; for (int idx = 1; idx <= maxVal; ++idx) { _array[_arrayIndex + idx] = val; val *= 1.0594634; } val = 0.94387404038686; for (int idx = -1; idx >= minVal; --idx) { _array[_arrayIndex + idx] = val; val *= 0.94387404038686; } } } // End of namespace Titanic