309 lines
8.2 KiB
C++
309 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "ultima/ultima8/audio/u8_music_process.h"
|
|
#include "ultima/ultima8/games/game_data.h"
|
|
#include "ultima/ultima8/audio/music_flex.h"
|
|
#include "ultima/ultima8/audio/midi_player.h"
|
|
#include "ultima/ultima8/audio/audio_mixer.h"
|
|
|
|
namespace Ultima {
|
|
namespace Ultima8 {
|
|
|
|
DEFINE_RUNTIME_CLASSTYPE_CODE(U8MusicProcess)
|
|
|
|
U8MusicProcess::U8MusicProcess() : _midiPlayer(nullptr), _state(PLAYBACK_NORMAL),
|
|
_currentTrack(0), _combatMusicActive(false),
|
|
_savedTrackState(nullptr) {
|
|
memset(_songBranches, (byte)-1, 128 * sizeof(int));
|
|
}
|
|
|
|
U8MusicProcess::U8MusicProcess(MidiPlayer *player) : _midiPlayer(player),
|
|
_state(PLAYBACK_NORMAL), _currentTrack(0), _combatMusicActive(false),
|
|
_savedTrackState(nullptr) {
|
|
memset(_songBranches, (byte)-1, 128 * sizeof(int));
|
|
|
|
_theMusicProcess = this;
|
|
_type = 1; // persistent
|
|
setRunPaused();
|
|
|
|
// Now get the transition midi
|
|
MusicFlex *musicflex = GameData::get_instance()->getMusic();
|
|
int xmidi_index = _midiPlayer->isFMSynth() ? 260 : 258;
|
|
MusicFlex::XMidiData *xmidi = musicflex->getXMidi(xmidi_index);
|
|
_midiPlayer->loadTransitionData(xmidi->_data, xmidi->_size);
|
|
}
|
|
|
|
U8MusicProcess::~U8MusicProcess() {
|
|
if (_savedTrackState)
|
|
delete _savedTrackState;
|
|
if (_midiPlayer)
|
|
_midiPlayer->stop();
|
|
_theMusicProcess = nullptr;
|
|
}
|
|
|
|
void U8MusicProcess::playMusic(int track) {
|
|
_trackState._lastRequest = track;
|
|
|
|
if (_combatMusicActive)
|
|
return;
|
|
|
|
if (_trackState._queued) {
|
|
_trackState._queued = track;
|
|
return;
|
|
}
|
|
|
|
playMusic_internal(track);
|
|
}
|
|
|
|
void U8MusicProcess::playCombatMusic(int track) {
|
|
_combatMusicActive = (track != 0);
|
|
playMusic_internal(track);
|
|
}
|
|
|
|
void U8MusicProcess::queueMusic(int track) {
|
|
if (_trackState._wanted != track) {
|
|
_trackState._queued = track;
|
|
}
|
|
}
|
|
|
|
void U8MusicProcess::unqueueMusic() {
|
|
_trackState._queued = 0;
|
|
}
|
|
|
|
void U8MusicProcess::restoreMusic() {
|
|
_combatMusicActive = false;
|
|
if (_trackState._queued) {
|
|
_trackState._queued = _trackState._lastRequest;
|
|
return;
|
|
}
|
|
|
|
playMusic_internal(_trackState._lastRequest);
|
|
}
|
|
|
|
void U8MusicProcess::fadeMusic(uint16 length) {
|
|
if (_midiPlayer && _midiPlayer->isPlaying())
|
|
_midiPlayer->startFadeOut(length);
|
|
}
|
|
|
|
bool U8MusicProcess::isFading() {
|
|
return _midiPlayer && _midiPlayer->isFading();
|
|
}
|
|
|
|
void U8MusicProcess::getTrackState(TrackState &trackState) const {
|
|
trackState = _trackState;
|
|
}
|
|
|
|
void U8MusicProcess::setTrackState(const TrackState &trackState) {
|
|
_trackState = trackState;
|
|
_state = PLAYBACK_PLAY_WANTED;
|
|
}
|
|
|
|
void U8MusicProcess::saveTrackState() {
|
|
assert(!_savedTrackState);
|
|
_savedTrackState = new TrackState(_trackState);
|
|
}
|
|
|
|
void U8MusicProcess::restoreTrackState() {
|
|
if (_savedTrackState == nullptr)
|
|
return;
|
|
|
|
_trackState = *_savedTrackState;
|
|
_state = PLAYBACK_PLAY_WANTED;
|
|
delete _savedTrackState;
|
|
_savedTrackState = nullptr;
|
|
}
|
|
|
|
void U8MusicProcess::playMusic_internal(int track) {
|
|
if (track < 0 || track >= 128) {
|
|
playMusic_internal(0);
|
|
return;
|
|
}
|
|
|
|
MusicFlex *musicflex = GameData::get_instance()->getMusic();
|
|
|
|
// No current track if not playing
|
|
if (_midiPlayer && !_midiPlayer->isPlaying())
|
|
_trackState._wanted = _currentTrack = 0;
|
|
|
|
// It's already playing and we are not transitioning
|
|
if (_currentTrack == track && _state == PLAYBACK_NORMAL) {
|
|
return;
|
|
} else if (_currentTrack == 0 || _state != PLAYBACK_NORMAL || !_midiPlayer) {
|
|
_trackState._wanted = track;
|
|
_state = PLAYBACK_PLAY_WANTED;
|
|
|
|
} else {
|
|
// We want to do a transition
|
|
const MusicFlex::SongInfo *info = musicflex->getSongInfo(_currentTrack);
|
|
|
|
uint32 measure = _midiPlayer->getSequenceCallbackData(0);
|
|
|
|
// No transition info, or invalid measure, so fast change
|
|
if (!info || (measure >= (uint32)info->_numMeasures) ||
|
|
!info->_transitions[track] || !info->_transitions[track][measure]) {
|
|
_currentTrack = 0;
|
|
if (track == 0) {
|
|
_trackState._wanted = 0;
|
|
_state = PLAYBACK_PLAY_WANTED;
|
|
} else {
|
|
playMusic_internal(track);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Get transition info
|
|
int trans = info->_transitions[track][measure];
|
|
bool overlay = false;
|
|
|
|
if (trans < 0) {
|
|
trans = (-trans) - 1;
|
|
overlay = true;
|
|
} else {
|
|
trans = trans - 1;
|
|
}
|
|
|
|
warning("Doing a MIDI transition! trans: %d overlay: %d", trans, overlay);
|
|
|
|
_midiPlayer->playTransition(trans, overlay);
|
|
|
|
_trackState._wanted = track;
|
|
_state = PLAYBACK_TRANSITION;
|
|
}
|
|
}
|
|
|
|
void U8MusicProcess::run() {
|
|
switch (_state) {
|
|
case PLAYBACK_NORMAL:
|
|
if (_midiPlayer && !_midiPlayer->isPlaying() && _trackState._queued) {
|
|
_trackState._wanted = _trackState._queued;
|
|
_state = PLAYBACK_PLAY_WANTED;
|
|
_trackState._queued = 0;
|
|
}
|
|
|
|
break;
|
|
|
|
case PLAYBACK_TRANSITION:
|
|
if (!_midiPlayer || !_midiPlayer->isPlaying()) {
|
|
// Transition has finished. Play the next track.
|
|
_state = PLAYBACK_PLAY_WANTED;
|
|
}
|
|
break;
|
|
|
|
case PLAYBACK_PLAY_WANTED: {
|
|
if (_midiPlayer)
|
|
_midiPlayer->stop();
|
|
|
|
MusicFlex::XMidiData *xmidi = nullptr;
|
|
|
|
if (_trackState._wanted) {
|
|
int xmidi_index = _trackState._wanted;
|
|
if (_midiPlayer && _midiPlayer->isFMSynth())
|
|
xmidi_index += 128;
|
|
|
|
xmidi = GameData::get_instance()->getMusic()->getXMidi(xmidi_index);
|
|
}
|
|
|
|
if (xmidi && xmidi->_data) {
|
|
|
|
if (_midiPlayer) {
|
|
// if there's a track queued, only play this one once
|
|
bool repeat = (_trackState._queued == 0);
|
|
_midiPlayer->load(xmidi->_data, xmidi->_size, 0);
|
|
_midiPlayer->setLooping(repeat);
|
|
if (_songBranches[_trackState._wanted] >= 0 && !_midiPlayer->hasBranchIndex(_songBranches[_trackState._wanted])) {
|
|
if (_songBranches[_trackState._wanted] == 0) {
|
|
// This track does not have any branches.
|
|
_songBranches[_trackState._wanted] = -1;
|
|
} else {
|
|
// Current branch is past the end of the list of branches. Reset to 0.
|
|
_songBranches[_trackState._wanted] = 0;
|
|
}
|
|
}
|
|
_midiPlayer->play(0, _songBranches[_trackState._wanted]);
|
|
}
|
|
|
|
_currentTrack = _trackState._wanted;
|
|
// Start this track at a different point (branch) next time
|
|
_songBranches[_trackState._wanted]++;
|
|
} else {
|
|
_currentTrack = _trackState._wanted = 0;
|
|
}
|
|
_state = PLAYBACK_NORMAL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void U8MusicProcess::saveData(Common::WriteStream *ws) {
|
|
MusicProcess::saveData(ws);
|
|
|
|
// When saving the game we want to remember the track state
|
|
// from before the menu was opened
|
|
const TrackState *stateToSave = _savedTrackState;
|
|
if (stateToSave == nullptr)
|
|
stateToSave = &_trackState;
|
|
|
|
ws->writeUint32LE(static_cast<uint32>(stateToSave->_wanted));
|
|
ws->writeUint32LE(static_cast<uint32>(stateToSave->_lastRequest));
|
|
ws->writeUint32LE(static_cast<uint32>(stateToSave->_queued));
|
|
}
|
|
|
|
bool U8MusicProcess::loadData(Common::ReadStream *rs, uint32 version) {
|
|
if (!MusicProcess::loadData(rs, version)) return false;
|
|
|
|
_trackState._wanted = static_cast<int32>(rs->readUint32LE());
|
|
|
|
if (version >= 4) {
|
|
_trackState._lastRequest = static_cast<int32>(rs->readUint32LE());
|
|
_trackState._queued = static_cast<int32>(rs->readUint32LE());
|
|
} else {
|
|
_trackState._lastRequest = _trackState._wanted;
|
|
_trackState._queued = 0;
|
|
}
|
|
|
|
_state = PLAYBACK_PLAY_WANTED;
|
|
|
|
_theMusicProcess = this;
|
|
|
|
_midiPlayer = AudioMixer::get_instance()->getMidiPlayer();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool U8MusicProcess::isPlaying() {
|
|
return _currentTrack != 0;
|
|
}
|
|
|
|
void U8MusicProcess::pauseMusic() {
|
|
// probably no real use for this?
|
|
warning("TODO: U8MusicProcess::pauseMusic Implement me.");
|
|
}
|
|
|
|
void U8MusicProcess::unpauseMusic() {
|
|
// probably no real use for this?
|
|
warning("TODO: U8MusicProcess::unpauseMusic Implement me.");
|
|
}
|
|
|
|
|
|
} // End of namespace Ultima8
|
|
} // End of namespace Ultima
|