mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-28 20:55:19 +00:00
9b8c04045b
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
226 lines
5.1 KiB
C++
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
|