mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-03 09:23:37 +00:00
322 lines
8.2 KiB
C++
322 lines
8.2 KiB
C++
/* 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 "bladerunner/music.h"
|
|
|
|
#include "bladerunner/audio_mixer.h"
|
|
#include "bladerunner/aud_stream.h"
|
|
#include "bladerunner/bladerunner.h"
|
|
#include "bladerunner/game_info.h"
|
|
#include "bladerunner/savefile.h"
|
|
#include "bladerunner/game_constants.h"
|
|
|
|
#include "common/timer.h"
|
|
namespace BladeRunner {
|
|
|
|
Music::Music(BladeRunnerEngine *vm) {
|
|
_vm = vm;
|
|
_channel = -1;
|
|
_musicVolume = BLADERUNNER_ORIGINAL_SETTINGS ? 65 : 100;
|
|
_isPlaying = false;
|
|
_isPaused = false;
|
|
_current.loop = false;
|
|
_isNextPresent = false;
|
|
_data = nullptr;
|
|
_stream = nullptr;
|
|
}
|
|
|
|
Music::~Music() {
|
|
stop(0);
|
|
while (isPlaying()) {
|
|
// wait for the mixer to finish
|
|
}
|
|
|
|
_vm->getTimerManager()->removeTimerProc(timerCallbackFadeOut);
|
|
_vm->getTimerManager()->removeTimerProc(timerCallbackNext);
|
|
}
|
|
|
|
bool Music::play(const Common::String &trackName, int volume, int pan, int32 timeFadeIn, int32 timePlay, int loop, int32 timeFadeOut) {
|
|
//Common::StackLock lock(_mutex);
|
|
|
|
if (_musicVolume <= 0) {
|
|
return false;
|
|
}
|
|
|
|
int volumeAdjusted = volume * _musicVolume / 100;
|
|
int volumeStart = volumeAdjusted;
|
|
if (timeFadeIn > 0) {
|
|
volumeStart = 1;
|
|
}
|
|
|
|
if (isPlaying()) {
|
|
if (!_current.name.equalsIgnoreCase(trackName)) {
|
|
_next.name = trackName;
|
|
_next.volume = volume;
|
|
_next.pan = pan;
|
|
_next.timeFadeIn = timeFadeIn;
|
|
_next.timePlay = timePlay;
|
|
_next.loop = loop;
|
|
_next.timeFadeOut = timeFadeOut;
|
|
if (_isNextPresent) {
|
|
stop(2);
|
|
}
|
|
_isNextPresent = true;
|
|
} else {
|
|
_current.loop = loop;
|
|
if (timeFadeIn < 0) {
|
|
timeFadeIn = 0;
|
|
}
|
|
adjustVolume(volumeAdjusted, timeFadeIn);
|
|
adjustPan(volumeAdjusted, timeFadeIn);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
_data = getData(trackName);
|
|
if (_data == nullptr) {
|
|
return false;
|
|
}
|
|
_stream = new AudStream(_data);
|
|
|
|
_isNextPresent = false;
|
|
_channel = _vm->_audioMixer->playMusic(_stream, volumeStart, mixerChannelEnded, this);
|
|
if (_channel < 0) {
|
|
delete _stream;
|
|
_stream = nullptr;
|
|
delete[] _data;
|
|
_data = nullptr;
|
|
|
|
return false;
|
|
}
|
|
if (timeFadeIn > 0) {
|
|
adjustVolume(volumeAdjusted, timeFadeIn);
|
|
}
|
|
_current.name = trackName;
|
|
if (timePlay > 0) {
|
|
_vm->getTimerManager()->removeTimerProc(timerCallbackFadeOut);
|
|
_vm->getTimerManager()->installTimerProc(timerCallbackFadeOut, timePlay * 1000 * 1000, this, "BladeRunnerMusicFadeoutTimer");
|
|
} else if (timeFadeOut > 0) {
|
|
_vm->getTimerManager()->removeTimerProc(timerCallbackFadeOut);
|
|
_vm->getTimerManager()->installTimerProc(timerCallbackFadeOut, (_stream->getLength() - timeFadeOut * 1000) * 1000, this, "BladeRunnerMusicFadeoutTimer");
|
|
}
|
|
_isPlaying = true;
|
|
_current.volume = volume;
|
|
_current.pan = pan;
|
|
_current.timeFadeIn = timeFadeIn;
|
|
_current.timePlay = timePlay;
|
|
_current.loop = loop;
|
|
_current.timeFadeOut = timeFadeOut;
|
|
return true;
|
|
}
|
|
|
|
void Music::stop(uint32 delay) {
|
|
Common::StackLock lock(_mutex);
|
|
|
|
if (_channel < 0) {
|
|
return;
|
|
}
|
|
|
|
#if !BLADERUNNER_ORIGINAL_BUGS
|
|
// In original game, on queued music was not removed and it started playing after actor left the scene
|
|
_isNextPresent = false;
|
|
#endif
|
|
|
|
_current.loop = false;
|
|
_vm->_audioMixer->stop(_channel, 60u * delay);
|
|
}
|
|
|
|
void Music::adjust(int volume, int pan, uint32 delay) {
|
|
if (volume != -1) {
|
|
adjustVolume(_musicVolume * volume/ 100, delay);
|
|
}
|
|
if (pan != -101) {
|
|
adjustPan(pan, delay);
|
|
}
|
|
}
|
|
|
|
bool Music::isPlaying() {
|
|
return _channel >= 0 && _isPlaying;
|
|
}
|
|
|
|
void Music::setVolume(int volume) {
|
|
_musicVolume = volume;
|
|
if (volume <= 0) {
|
|
stop(2);
|
|
} else if (isPlaying()) {
|
|
_vm->_audioMixer->adjustVolume(_channel, _musicVolume * _current.volume / 100, 120);
|
|
}
|
|
}
|
|
|
|
int Music::getVolume() {
|
|
return _musicVolume;
|
|
}
|
|
|
|
void Music::playSample() {
|
|
if (!isPlaying()) {
|
|
play(_vm->_gameInfo->getSfxTrack(kSfxMUSVOL8), 100, 0, 2, -1, 0, 3);
|
|
}
|
|
}
|
|
|
|
void Music::save(SaveFileWriteStream &f) {
|
|
f.writeBool(_isNextPresent);
|
|
f.writeBool(_isPlaying);
|
|
f.writeBool(_isPaused);
|
|
f.writeStringSz(_current.name, 13);
|
|
f.writeInt(_current.volume);
|
|
f.writeInt(_current.pan);
|
|
f.writeInt(_current.timeFadeIn);
|
|
f.writeInt(_current.timePlay);
|
|
f.writeInt(_current.loop);
|
|
f.writeInt(_current.timeFadeOut);
|
|
f.writeStringSz(_next.name, 13);
|
|
f.writeInt(_next.volume);
|
|
f.writeInt(_next.pan);
|
|
f.writeInt(_next.timeFadeIn);
|
|
f.writeInt(_next.timePlay);
|
|
f.writeInt(_next.loop);
|
|
f.writeInt(_next.timeFadeOut);
|
|
}
|
|
|
|
void Music::load(SaveFileReadStream &f) {
|
|
_isNextPresent = f.readBool();
|
|
_isPlaying = f.readBool();
|
|
_isPaused = f.readBool();
|
|
_current.name = f.readStringSz(13);
|
|
_current.volume = f.readInt();
|
|
_current.pan = f.readInt();
|
|
_current.timeFadeIn = f.readInt();
|
|
_current.timePlay = f.readInt();
|
|
_current.loop = f.readInt();
|
|
_current.timeFadeOut = f.readInt();
|
|
_next.name = f.readStringSz(13);
|
|
_next.volume = f.readInt();
|
|
_next.pan = f.readInt();
|
|
_next.timeFadeIn = f.readInt();
|
|
_next.timePlay = f.readInt();
|
|
_next.loop = f.readInt();
|
|
_next.timeFadeOut = f.readInt();
|
|
|
|
stop(2);
|
|
if (_isPlaying) {
|
|
if (_channel == -1) {
|
|
play(_current.name,
|
|
_current.volume,
|
|
_current.pan,
|
|
_current.timeFadeIn,
|
|
_current.timePlay,
|
|
_current.loop,
|
|
_current.timeFadeOut);
|
|
} else {
|
|
_isNextPresent = true;
|
|
_next.name = _current.name;
|
|
_next.volume = _current.volume;
|
|
_next.pan = _current.pan;
|
|
_next.timeFadeIn = _current.timeFadeIn;
|
|
_next.timePlay = _current.timePlay;
|
|
_next.loop = _current.loop;
|
|
_next.timeFadeOut = _current.timeFadeOut;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Music::adjustVolume(int volume, uint32 delay) {
|
|
if (_channel >= 0) {
|
|
_vm->_audioMixer->adjustVolume(_channel, volume, delay);
|
|
}
|
|
}
|
|
|
|
void Music::adjustPan(int pan, uint32 delay) {
|
|
if (_channel >= 0) {
|
|
_vm->_audioMixer->adjustPan(_channel, pan, delay);
|
|
}
|
|
}
|
|
|
|
void Music::ended() {
|
|
Common::StackLock lock(_mutex);
|
|
|
|
_isPlaying = false;
|
|
_channel = -1;
|
|
|
|
delete[] _data;
|
|
_data = nullptr;
|
|
|
|
_vm->getTimerManager()->installTimerProc(timerCallbackNext, 100 * 1000, this, "BladeRunnerMusicNextTimer");
|
|
}
|
|
|
|
void Music::fadeOut() {
|
|
_vm->getTimerManager()->removeTimerProc(timerCallbackFadeOut);
|
|
if (_channel >= 0) {
|
|
if (_current.timeFadeOut < 0) {
|
|
_current.timeFadeOut = 0;
|
|
}
|
|
_vm->_audioMixer->stop(_channel, 60u * _current.timeFadeOut);
|
|
}
|
|
}
|
|
|
|
void Music::next() {
|
|
_vm->getTimerManager()->removeTimerProc(timerCallbackNext);
|
|
|
|
if (_isNextPresent) {
|
|
if (_isPaused) {
|
|
_vm->getTimerManager()->installTimerProc(timerCallbackNext, 2000 * 1000, this, "BladeRunnerMusicNextTimer");
|
|
} else {
|
|
play(_next.name.c_str(), _next.volume, _next.pan, _next.timeFadeIn, _next.timePlay, _next.loop, _next.timeFadeOut);
|
|
}
|
|
_current.loop = false;
|
|
} else if (_current.loop) {
|
|
play(_current.name.c_str(), _current.volume, _current.pan, _current.timeFadeIn, _current.timePlay, _current.loop, _current.timeFadeOut);
|
|
}
|
|
}
|
|
|
|
void Music::mixerChannelEnded(int channel, void *data) {
|
|
if (data != nullptr) {
|
|
((Music *)data)->ended();
|
|
}
|
|
}
|
|
|
|
void Music::timerCallbackFadeOut(void *refCon) {
|
|
((Music *)refCon)->fadeOut();
|
|
}
|
|
|
|
void Music::timerCallbackNext(void *refCon) {
|
|
((Music *)refCon)->next();
|
|
}
|
|
|
|
byte *Music::getData(const Common::String &name) {
|
|
// NOTE: This is not part original game, loading data is done in the mixer and its using buffering to limit memory usage
|
|
Common::SeekableReadStream *stream = _vm->getResourceStream(name);
|
|
|
|
if (stream == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
uint32 size = stream->size();
|
|
byte *data = new byte[size];
|
|
stream->read(data, size);
|
|
|
|
delete stream;
|
|
|
|
return data;
|
|
}
|
|
|
|
} // End of namespace BladeRunner
|