scummvm/engines/made/music.cpp
Filippos Karapetis 9b8c04045b MIDI: Fix for bug #3170988 - "MONKEY2: Messed up MT-32 music"
This is a regression from r55256. Apparently, SCUMM has issues when sending a sustain
off on a notes off event. Thus, this has been turned into a feature, which is disabled
by default. Since MADE, SAGA and tinsel all share the same music code and play regular
MIDI files, and this feature fixes hanging notes for them, it has been enabled for them.
Also, applied a patch for a bug regarding the notes off event in MADE and tinsel,
which has been applied in SAGA already

svn-id: r55746
2011-02-02 23:27:59 +00:00

226 lines
5.1 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.
*
* $URL$
* $Id$
*
*/
// FIXME: This code is taken from SAGA and needs more work (e.g. setVolume).
// MIDI and digital music class
#include "sound/audiostream.h"
#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "made/music.h"
namespace Made {
MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false), _passThrough(false), _isGM(false) {
memset(_channel, 0, sizeof(_channel));
_masterVolume = 0;
_xmidiParser = MidiParser::createParser_XMIDI();
_smfParser = MidiParser::createParser_SMF();
}
MusicPlayer::~MusicPlayer() {
_driver->setTimerCallback(NULL, NULL);
stop();
this->close();
_xmidiParser->setMidiDriver(NULL);
_smfParser->setMidiDriver(NULL);
delete _xmidiParser;
delete _smfParser;
}
void MusicPlayer::setVolume(int volume) {
volume = CLIP(volume, 0, 255);
if (_masterVolume == volume)
return;
_masterVolume = volume;
Common::StackLock lock(_mutex);
for (int i = 0; i < 16; ++i) {
if (_channel[i]) {
_channel[i]->volume(_channelVolume[i] * _masterVolume / 255);
}
}
}
int MusicPlayer::open() {
// Don't ever call open without first setting the output driver!
if (!_driver)
return 255;
int ret = _driver->open();
if (ret)
return ret;
if (_nativeMT32)
_driver->sendMT32Reset();
else
_driver->sendGMReset();
_driver->setTimerCallback(this, &onTimer);
return 0;
}
void MusicPlayer::close() {
stop();
if (_driver)
_driver->close();
_driver = 0;
}
void MusicPlayer::send(uint32 b) {
if (_passThrough) {
_driver->send(b);
return;
}
byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) {
// Adjust volume changes by master volume
byte volume = (byte)((b >> 16) & 0x7F);
_channelVolume[channel] = volume;
volume = volume * _masterVolume / 255;
b = (b & 0xFF00FFFF) | (volume << 16);
} else if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
}
else if ((b & 0xFFF0) == 0x007BB0) {
// Only respond to All Notes Off if this channel
// has currently been allocated
if (!_channel[b & 0x0F])
return;
}
if (!_channel[channel])
_channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
if (_channel[channel])
_channel[channel]->send(b);
}
void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
switch (type) {
case 0x2F: // End of Track
if (_looping)
_parser->jumpToTick(0);
else
stop();
break;
default:
//warning("Unhandled meta event: %02x", type);
break;
}
}
void MusicPlayer::onTimer(void *refCon) {
MusicPlayer *music = (MusicPlayer *)refCon;
Common::StackLock lock(music->_mutex);
if (music->_isPlaying)
music->_parser->onTimer();
}
void MusicPlayer::playXMIDI(GenericResource *midiResource, MusicFlags flags) {
if (_isPlaying)
return;
stop();
// Load XMID resource data
_isGM = true;
if (_xmidiParser->loadMusic(midiResource->getData(), midiResource->getSize())) {
MidiParser *parser = _xmidiParser;
parser->setTrack(0);
parser->setMidiDriver(this);
parser->setTimerRate(getBaseTempo());
parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
_parser = parser;
setVolume(127);
_looping = flags & MUSIC_LOOP;
_isPlaying = true;
}
}
void MusicPlayer::playSMF(GenericResource *midiResource, MusicFlags flags) {
if (_isPlaying)
return;
stop();
// Load MIDI resource data
_isGM = true;
if (_smfParser->loadMusic(midiResource->getData(), midiResource->getSize())) {
MidiParser *parser = _smfParser;
parser->setTrack(0);
parser->setMidiDriver(this);
parser->setTimerRate(getBaseTempo());
parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
_parser = parser;
setVolume(127);
_looping = flags & MUSIC_LOOP;
_isPlaying = true;
}
}
void MusicPlayer::stop() {
Common::StackLock lock(_mutex);
_isPlaying = false;
if (_parser) {
_parser->unloadMusic();
_parser = NULL;
}
}
void MusicPlayer::pause() {
setVolume(-1);
_isPlaying = false;
}
void MusicPlayer::resume() {
setVolume(127);
_isPlaying = true;
}
} // End of namespace Made