mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
278 lines
7.2 KiB
C++
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
|