scummvm/engines/scumm/players/player_he.cpp
2023-12-24 13:19:25 +01:00

237 lines
5.5 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 "scumm/players/player_he.h"
#include "scumm/scumm.h"
#include "scumm/file.h"
#include "audio/miles.h"
#include "audio/midiparser.h"
#include "audio/mixer.h"
#include "common/memstream.h"
namespace Scumm {
Player_HE::Player_HE(ScummEngine *scumm) :
_vm(scumm),
_currentMusic(-1),
_bank(NULL),
_parser(NULL),
_midi(NULL),
_masterVolume(256) {
for (int chan = 0; chan < 16; chan++)
_channelVolume[chan] = 127;
loadAdLibBank();
Common::MemoryReadStream *bankStream = new Common::MemoryReadStream(_bank, _bankSize);
_midi = Audio::MidiDriver_Miles_AdLib_create("", "", bankStream);
if (!_midi) {
error("Player_HE::Player_HE: could not create midi driver");
}
if (_midi->open() != 0) {
error("Player_HE::Player_HE: could not open midi driver");
}
}
Player_HE::~Player_HE() {
if (_parser) {
_parser->stopPlaying();
delete _parser;
_parser = NULL;
}
if (_midi) {
_midi->setTimerCallback(0, 0);
_midi->close();
delete _midi;
_midi = NULL;
}
if (_bank) {
free(_bank);
}
}
void Player_HE::setMusicVolume(int vol) {
_masterVolume = vol;
for (int chan = 0; chan < 16; chan++) {
byte volume = (_channelVolume[chan] * vol) / 256;
if (_midi)
_midi->send(0x07b0 | chan | (volume << 16));
}
}
void Player_HE::onTimer(void *data) {
Player_HE *player = (Player_HE *)data;
Common::StackLock lock(player->_mutex);
if (player->_parser)
player->_parser->onTimer();
}
void Player_HE::startSoundWithTrackID(int sound, int track) {
Common::StackLock lock(_mutex);
byte *ptr = _vm->getResourceAddress(rtSound, sound);
if (ptr == NULL)
return;
if (_parser) {
_parser->stopPlaying();
delete _parser;
}
_parser = MidiParser::createParser_XMIDI();
_parser->setMidiDriver(this);
_parser->loadMusic(ptr + 40, 0);
_parser->setTrack(track);
_parser->setTimerRate(_midi->getBaseTempo());
_midi->setTimerCallback(this, &Player_HE::onTimer);
_currentMusic = sound;
}
void Player_HE::stopSound(int sound) {
Common::StackLock lock(_mutex);
if (!_parser || _currentMusic != sound)
return;
_parser->stopPlaying();
delete _parser;
_parser = NULL;
}
void Player_HE::stopAllSounds() {
Common::StackLock lock(_mutex);
if (!_parser)
return;
_parser->stopPlaying();
delete _parser;
_parser = NULL;
}
int Player_HE::getSoundStatus(int sound) const {
Common::StackLock lock(_mutex);
return (_parser && _currentMusic == sound) ? _parser->isPlaying() : 0;
}
int Player_HE::getMusicTimer() {
Common::StackLock lock(_mutex);
return _parser ? _parser->getTick() : 0;
}
void Player_HE::loadAdLibBank() {
ScummFile file(_vm);
Common::String drvName;
char entryName[14];
uint32 tag, entrySize, fileSize;
Common::String bankName;
if (_vm->_game.id == GID_PUTTMOON) {
// Use GM bank
bankName = "FAT.AD";
} else {
// Use MT32-like bank
bankName = "MIDPAK.AD";
}
const char *ptr = strchr(_vm->_filenamePattern.pattern, '.');
if (ptr) {
drvName = Common::String(_vm->_filenamePattern.pattern, ptr - _vm->_filenamePattern.pattern + 1);
} else {
drvName = _vm->_filenamePattern.pattern;
drvName += '.';
}
drvName += "drv";
if (!file.open(Common::Path(drvName)))
error("Player_HE::loadAdLibBank(): could not open %s", drvName.c_str());
uint32 size = (uint32)file.size();
for (uint32 offset = 0; offset < size;) {
file.seek(offset, SEEK_SET);
if (size - offset < 31)
error("Player_HE::loadAdLibBank(): unexpected end of file");
tag = file.readUint32BE();
entrySize = file.readUint32BE();
if (size - offset < entrySize)
error("Player_HE::loadAdLibBank(): unexpected end of file");
fileSize = entrySize - 31;
file.read(entryName, 13);
entryName[13] = 0;
if (tag != MKTAG('F', 'I', 'L', 'E'))
error("Player_HE::loadAdLibBank(): unknown entry format");
if (entryName == bankName) {
_bank = (byte*)malloc(fileSize);
file.read(_bank, fileSize);
_bankSize = fileSize;
return;
}
offset += entrySize;
}
error("Player_HE::loadAdLibBank(): could not find %s entry", bankName.c_str());
}
int Player_HE::open() {
if (_midi)
return _midi->open();
return 0;
}
bool Player_HE::isOpen() const {
if (_midi)
return _midi->isOpen();
return false;
}
void Player_HE::close() {
if (_midi)
_midi->close();
}
void Player_HE::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
if (_midi)
_midi->setTimerCallback(timerParam, timerProc);
}
uint32 Player_HE::getBaseTempo() {
if (_midi)
return _midi->getBaseTempo();
return 0;
}
void Player_HE::send(uint32 b) {
byte chan = b & 0x0f;
byte cmd = b & 0xf0;
byte op1 = (b >> 8) & 0x7f;
byte op2 = (b >> 16) & 0x7f;
if (cmd == 0xb0 && op1 == 0x07) {
_channelVolume[chan] = op2;
op2 = (op2 * _masterVolume) / 256;
b = (b & 0xffff) | (op2 << 16);
}
if (_midi)
_midi->send(b);
}
}