Initial commit
This commit is contained in:
308
engines/ultima/ultima8/audio/u8_music_process.cpp
Normal file
308
engines/ultima/ultima8/audio/u8_music_process.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
/* 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
|
||||
Reference in New Issue
Block a user