Files
scummvm/engines/agos/drivers/accolade/mt32.cpp
T

278 lines
7.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 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.
*
*/
#include "agos/drivers/accolade/mididriver.h"
#include "audio/mididrv.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "common/mutex.h"
#include "common/system.h"
#include "common/textconsole.h"
namespace AGOS {
class MidiDriver_Accolade_MT32 : public MidiDriver {
public:
MidiDriver_Accolade_MT32();
virtual ~MidiDriver_Accolade_MT32();
// MidiDriver
int open();
void close();
bool isOpen() const { return _isOpen; }
void send(uint32 b);
MidiChannel *allocateChannel() {
if (_driver)
return _driver->allocateChannel();
return NULL;
}
MidiChannel *getPercussionChannel() {
if (_driver)
return _driver->getPercussionChannel();
return NULL;
}
void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
if (_driver)
_driver->setTimerCallback(timer_param, timer_proc);
}
uint32 getBaseTempo() {
if (_driver) {
return _driver->getBaseTempo();
}
return 1000000 / _baseFreq;
}
protected:
Common::Mutex _mutex;
MidiDriver *_driver;
bool _nativeMT32; // native MT32, may also be our MUNT, or MUNT over MIDI
bool _isOpen;
int _baseFreq;
private:
// simple mapping between MIDI channel and MT32 channel
byte _channelMapping[AGOS_MIDI_CHANNEL_COUNT];
// simple mapping between MIDI instruments and MT32 instruments
byte _instrumentMapping[AGOS_MIDI_INSTRUMENT_COUNT];
public:
bool setupInstruments(byte *instrumentData, uint16 instrumentDataSize, bool useMusicDrvFile);
};
MidiDriver_Accolade_MT32::MidiDriver_Accolade_MT32() {
_driver = NULL;
_isOpen = false;
_nativeMT32 = false;
_baseFreq = 250;
memset(_channelMapping, 0, sizeof(_channelMapping));
memset(_instrumentMapping, 0, sizeof(_instrumentMapping));
}
MidiDriver_Accolade_MT32::~MidiDriver_Accolade_MT32() {
Common::StackLock lock(_mutex);
if (_driver) {
_driver->setTimerCallback(0, 0);
_driver->close();
delete _driver;
}
_driver = NULL;
}
int MidiDriver_Accolade_MT32::open() {
assert(!_driver);
// debugC(kDebugLevelMT32Driver, "MT32: starting driver");
// Setup midi driver
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_MT32);
MusicType musicType = MidiDriver::getMusicType(dev);
// check, if we got a real MT32 (or MUNT, or MUNT over MIDI)
switch (musicType) {
case MT_MT32:
_nativeMT32 = true;
break;
case MT_GM:
if (ConfMan.getBool("native_mt32")) {
_nativeMT32 = true;
}
break;
default:
break;
}
_driver = MidiDriver::createMidi(dev);
if (!_driver)
return 255;
int ret = _driver->open();
if (ret)
return ret;
if (_nativeMT32)
_driver->sendMT32Reset();
else
_driver->sendGMReset();
return 0;
}
void MidiDriver_Accolade_MT32::close() {
if (_driver) {
_driver->close();
}
}
// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php
void MidiDriver_Accolade_MT32::send(uint32 b) {
byte command = b & 0xf0;
byte channel = b & 0xf;
if (command == 0xF0) {
if (_driver) {
_driver->send(b);
}
return;
}
byte mappedChannel = _channelMapping[channel];
if (mappedChannel < AGOS_MIDI_CHANNEL_COUNT) {
// channel mapped to an actual MIDI channel, so use that one
b = (b & 0xFFFFFFF0) | mappedChannel;
if (command == 0xC0) {
// Program change
// Figure out the requested instrument
byte midiInstrument = (b >> 8) & 0xFF;
byte mappedInstrument = _instrumentMapping[midiInstrument];
// If there is no actual MT32 (or MUNT), we make a second mapping to General MIDI instruments
if (!_nativeMT32) {
mappedInstrument = (MidiDriver::_mt32ToGm[mappedInstrument]);
}
// And replace it
b = (b & 0xFFFF00FF) | (mappedInstrument << 8);
}
if (_driver) {
_driver->send(b);
}
}
}
// Called right at the start, we get an INSTR.DAT entry
bool MidiDriver_Accolade_MT32::setupInstruments(byte *driverData, uint16 driverDataSize, bool useMusicDrvFile) {
uint16 channelMappingOffset = 0;
uint16 channelMappingSize = 0;
uint16 instrumentMappingOffset = 0;
uint16 instrumentMappingSize = 0;
if (!useMusicDrvFile) {
// INSTR.DAT: we expect at least 354 bytes
if (driverDataSize < 354)
return false;
// Data is like this:
// 128 bytes instrument mapping
// 128 bytes instrument volume adjust (signed!) (not used for MT32)
// 16 bytes unknown
// 16 bytes channel mapping
// 64 bytes key note mapping (not really used for MT32)
// 1 byte instrument count
// 1 byte bytes per instrument
// x bytes no instruments used for MT32
channelMappingOffset = 256 + 16;
channelMappingSize = 16;
instrumentMappingOffset = 0;
instrumentMappingSize = 128;
} else {
// MUSIC.DRV: we expect at least 468 bytes
if (driverDataSize < 468)
return false;
channelMappingOffset = 396;
channelMappingSize = 16;
instrumentMappingOffset = 140;
instrumentMappingSize = 128;
}
// Channel mapping
if (channelMappingSize) {
// Get these 16 bytes for MIDI channel mapping
if (channelMappingSize != sizeof(_channelMapping))
return false;
memcpy(_channelMapping, driverData + channelMappingOffset, sizeof(_channelMapping));
} else {
// Set up straight mapping
for (uint16 channelNr = 0; channelNr < sizeof(_channelMapping); channelNr++) {
_channelMapping[channelNr] = channelNr;
}
}
if (instrumentMappingSize) {
// And these for instrument mapping
if (instrumentMappingSize > sizeof(_instrumentMapping))
return false;
memcpy(_instrumentMapping, driverData + instrumentMappingOffset, instrumentMappingSize);
}
// Set up straight mapping for the remaining data
for (uint16 instrumentNr = instrumentMappingSize; instrumentNr < sizeof(_instrumentMapping); instrumentNr++) {
_instrumentMapping[instrumentNr] = instrumentNr;
}
return true;
}
MidiDriver *MidiDriver_Accolade_MT32_create(Common::String driverFilename) {
byte *driverData = NULL;
uint16 driverDataSize = 0;
bool isMusicDrvFile = false;
MidiDriver_Accolade_readDriver(driverFilename, MT_MT32, driverData, driverDataSize, isMusicDrvFile);
if (!driverData)
error("ACCOLADE-ADLIB: error during readDriver()");
MidiDriver_Accolade_MT32 *driver = new MidiDriver_Accolade_MT32();
if (driver) {
if (!driver->setupInstruments(driverData, driverDataSize, isMusicDrvFile)) {
delete driver;
driver = nullptr;
}
}
delete[] driverData;
return driver;
}
} // End of namespace AGOS