SHERLOCK: add (unfinished) adlib driver

This commit is contained in:
Martin Kiewitz 2015-05-30 01:48:30 +02:00
parent 508dc14e30
commit f80cc4a84d
4 changed files with 722 additions and 24 deletions

View file

@ -23,11 +23,14 @@
#include "common/config-manager.h"
#include "sherlock/sherlock.h"
#include "sherlock/music.h"
#include "sherlock/scalpel/drivers/mididriver.h"
namespace Sherlock {
#define NUM_SONGS 45
#define USE_SCI_MIDI_PLAYER 1
/* This tells which song to play in each room, 0 = no song played */
static const char ROOM_SONG[62] = {
0, 20, 43, 6, 11, 2, 8, 15, 6, 28,
@ -59,7 +62,7 @@ MidiParser_SH::MidiParser_SH() {
}
void MidiParser_SH::parseNextEvent(EventInfo &info) {
warning("parseNextEvent");
// warning("parseNextEvent");
// An attempt to remap MT32 instruments to GMIDI. Only partially successful, it still
// does not sound even close to the real MT32. Oddly enough, on the actual hardware MT32
@ -88,13 +91,15 @@ void MidiParser_SH::parseNextEvent(EventInfo &info) {
info.delta = 0;
info.event = *_position._playPos++;
warning("Event %x", info.event);
//warning("Event %x", info.event);
_position._runningStatus = info.event;
switch (info.command()) {
case 0xC: {
case 0xC: { // program change
int idx = *_position._playPos++;
info.basic.param1 = mt32Map[idx & 0x7f]; // remap MT32 to GM
info.basic.param1 = idx & 0x7f;
// don't do this here, it breaks adlib
//info.basic.param1 = mt32Map[idx & 0x7f]; // remap MT32 to GM
info.basic.param2 = 0;
}
break;
@ -160,14 +165,12 @@ bool MidiParser_SH::loadMusic(byte *data, uint32 size) {
warning("loadMusic");
unloadMusic();
byte *pos = data;
byte *headerPtr = data;
byte *pos = data;
uint16 headerSize = READ_LE_UINT16(headerPtr);
assert(headerSize == 0x7F);
if (memcmp(" ", pos, 12)) {
warning("Expected header not found in music file");
return false;
}
pos += 12;
byte headerSize = *pos;
// Skip over header
pos += headerSize;
_lastEvent = 0;
@ -190,16 +193,49 @@ Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
_vm->_res->addToCache("MUSIC.LIB");
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
_driver = MidiDriver::createMidi(dev);
assert(_driver);
int ret = _driver->open();
if (ret == 0) {
_driver->sendGMReset();
_driver->setTimerCallback(&_midiParser, &_midiParser.timerCallback);
_musicType = MidiDriver::getMusicType(dev);
#if USE_SCI_MIDI_PLAYER
_pMidiDrv = NULL;
#endif
_driver = NULL;
switch (_musicType) {
case MT_ADLIB:
#if USE_SCI_MIDI_PLAYER
_pMidiDrv = MidiPlayer_AdLib_create();
#else
_driver = MidiDriver_AdLib_create();
#endif
break;
default:
_driver = MidiDriver::createMidi(dev);
break;
}
#if USE_SCI_MIDI_PLAYER
if (_pMidiDrv) {
assert(_pMidiDrv);
int ret = _pMidiDrv->open();
if (ret == 0) {
_pMidiDrv->setTimerCallback(&_midiParser, &_midiParser.timerCallback);
}
_midiParser.setMidiDriver(_pMidiDrv);
_midiParser.setTimerRate(_pMidiDrv->getBaseTempo());
}
#endif
if (_driver) {
assert(_driver);
int ret = _driver->open();
if (ret == 0) {
_driver->sendGMReset();
_driver->setTimerCallback(&_midiParser, &_midiParser.timerCallback);
}
_midiParser.setMidiDriver(_driver);
_midiParser.setTimerRate(_driver->getBaseTempo());
}
_midiParser.setMidiDriver(_driver);
_midiParser.setTimerRate(_driver->getBaseTempo());
_musicPlaying = false;
_musicOn = true;
@ -248,17 +284,49 @@ bool Music::playMusic(const Common::String &name) {
Common::SeekableReadStream *stream = _vm->_res->load(name, "MUSIC.LIB");
byte *data = new byte[stream->size()];
byte *ptr = data;
int32 dataSize = stream->size();
assert(data);
stream->read(ptr, stream->size());
stream->read(data, dataSize);
// for dumping the music tracks
#if 0
Common::DumpFile outFile;
outFile.open(name + ".RAW");
outFile.write(data, stream->size());
outFile.flush();
outFile.close();
#endif
_midiParser.loadMusic(data, stream->size());
if (dataSize < 14) {
warning("not enough data in music file");
return false;
}
byte *dataPos = data;
if (memcmp(" ", dataPos, 12)) {
warning("Expected header not found in music file");
return false;
}
dataPos += 12;
dataSize -= 12;
uint16 headerSize = READ_LE_UINT16(dataPos);
if (headerSize != 0x7F) {
warning("music header is not as expected");
return false;
}
if (_musicType == MT_ADLIB) {
if (_driver)
MidiDriver_AdLib_newMusicData(_driver, dataPos, dataSize);
#if USE_SCI_MIDI_PLAYER
if (_pMidiDrv)
MidiPlayer_AdLib_newMusicData(_pMidiDrv, dataPos, dataSize);
#endif
}
_midiParser.loadMusic(dataPos, dataSize);
return true;
}

View file

@ -25,7 +25,8 @@
#include "audio/midiplayer.h"
#include "audio/midiparser.h"
#include "audio/mididrv.h"
//#include "audio/mididrv.h"
#include "sherlock/scalpel/drivers/mididriver.h"
namespace Sherlock {
@ -44,15 +45,21 @@ public:
virtual bool loadMusic(byte *data, uint32 size);
};
class Music : public Audio::MidiPlayer {
class Music {
private:
SherlockEngine *_vm;
Audio::Mixer *_mixer;
MidiParser_SH _midiParser;
MidiPlayer *_pMidiDrv;
MidiDriver *_driver;
public:
bool _musicPlaying;
bool _musicOn;
private:
MusicType _musicType;
public:
Music(SherlockEngine *vm, Audio::Mixer *mixer);

View file

@ -0,0 +1,532 @@
/* 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 "sherlock/sherlock.h"
#include "sherlock/scalpel/drivers/mididriver.h"
#include "common/file.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "audio/fmopl.h"
#include "audio/softsynth/emumidi.h"
namespace Sherlock {
#define USE_SCI_MIDI_PLAYER 1
#define SHERLOCK_ADLIB_VOICES_COUNT 9
#define SHERLOCK_ADLIB_NOTES_COUNT 96
byte adlib_Operator1Register[SHERLOCK_ADLIB_VOICES_COUNT] = {
0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12
};
byte adlib_Operator2Register[SHERLOCK_ADLIB_VOICES_COUNT] = {
0x03, 0x04, 0x05, 0x0B, 0x0C, 0x0D, 0x13, 0x14, 0x15
};
struct adlib_InstrumentEntry {
byte reg20op1;
byte reg40op1;
byte reg60op1;
byte reg80op1;
byte regE0op1;
byte reg20op2;
byte reg40op2;
byte reg60op2;
byte reg80op2;
byte regE0op2;
byte regC0;
byte frequencyAdjust;
};
// hardcoded, dumped from ADHOM.DRV
const adlib_InstrumentEntry adlib_instrumentTable[] = {
{ 0x71, 0x89, 0x51, 0x11, 0x00, 0x61, 0x23, 0x42, 0x15, 0x01, 0x02, 0xF4 },
{ 0x22, 0x20, 0x97, 0x89, 0x00, 0xA2, 0x1F, 0x70, 0x07, 0x00, 0x0A, 0xF4 },
{ 0x70, 0x1A, 0x64, 0x13, 0x00, 0x20, 0x1F, 0x53, 0x46, 0x00, 0x0E, 0xF4 },
{ 0xB6, 0x4A, 0xB6, 0x32, 0x00, 0x11, 0x2B, 0xD1, 0x31, 0x00, 0x0E, 0xE8 },
{ 0x71, 0x8B, 0x51, 0x11, 0x00, 0x61, 0x20, 0x32, 0x35, 0x01, 0x02, 0xF4 },
{ 0x71, 0x8A, 0x51, 0x11, 0x00, 0x61, 0x20, 0x32, 0x25, 0x01, 0x02, 0xF4 },
{ 0x23, 0x0F, 0xF4, 0x04, 0x02, 0x2F, 0x25, 0xF0, 0x43, 0x00, 0x06, 0xE8 },
{ 0x71, 0x1C, 0x71, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 },
{ 0x71, 0x8A, 0x6E, 0x17, 0x00, 0x25, 0x27, 0x6B, 0x0E, 0x00, 0x02, 0xF4 },
{ 0x71, 0x1D, 0x81, 0x03, 0x00, 0x21, 0x1F, 0x64, 0x17, 0x00, 0x0E, 0xF4 },
{ 0x01, 0x4B, 0xF1, 0x50, 0x00, 0x01, 0x23, 0xD2, 0x76, 0x00, 0x06, 0xF4 },
{ 0x2F, 0xCA, 0xF8, 0xE5, 0x00, 0x21, 0x1F, 0xC0, 0xFF, 0x00, 0x00, 0xF4 },
{ 0x29, 0xCD, 0xF0, 0x91, 0x00, 0x21, 0x1F, 0xE0, 0x86, 0x00, 0x02, 0xF4 },
{ 0x24, 0xD0, 0xF0, 0x01, 0x00, 0x21, 0x1F, 0xE0, 0x86, 0x00, 0x02, 0xF4 },
{ 0x23, 0xC8, 0xF0, 0x01, 0x00, 0x21, 0x1F, 0xE0, 0x86, 0x00, 0x02, 0xF4 },
{ 0x64, 0xC9, 0xB0, 0x01, 0x00, 0x61, 0x1F, 0xF0, 0x86, 0x00, 0x02, 0xF4 },
{ 0x33, 0x85, 0xA1, 0x10, 0x00, 0x15, 0x9F, 0x72, 0x23, 0x00, 0x08, 0xF4 },
{ 0x31, 0x85, 0xA1, 0x10, 0x00, 0x15, 0x9F, 0x73, 0x33, 0x00, 0x08, 0xF4 },
{ 0x31, 0x81, 0xA1, 0x30, 0x00, 0x16, 0x9F, 0xC2, 0x74, 0x00, 0x08, 0xF4 },
{ 0x03, 0x8A, 0xF0, 0x7B, 0x00, 0x02, 0x9F, 0xF4, 0x7B, 0x00, 0x08, 0xF4 },
{ 0x03, 0x8A, 0xF0, 0x7B, 0x00, 0x01, 0x9F, 0xF4, 0x7B, 0x00, 0x08, 0xF4 },
{ 0x23, 0x8A, 0xF2, 0x7B, 0x00, 0x01, 0x9F, 0xF4, 0x7B, 0x00, 0x08, 0xF4 },
{ 0x32, 0x80, 0x01, 0x10, 0x00, 0x12, 0x9F, 0x72, 0x33, 0x00, 0x08, 0xF4 },
{ 0x32, 0x80, 0x01, 0x10, 0x00, 0x14, 0x9F, 0x73, 0x33, 0x00, 0x08, 0xF4 },
{ 0x31, 0x16, 0x73, 0x8E, 0x00, 0x21, 0x1F, 0x80, 0x9E, 0x00, 0x0E, 0xF4 },
{ 0x30, 0x16, 0x73, 0x7E, 0x00, 0x21, 0x1F, 0x80, 0x9E, 0x00, 0x0E, 0x00 },
{ 0x31, 0x94, 0x33, 0x73, 0x00, 0x21, 0x1F, 0xA0, 0x97, 0x00, 0x0E, 0xF4 },
{ 0x31, 0x94, 0xD3, 0x73, 0x00, 0x21, 0x20, 0xA0, 0x97, 0x00, 0x0E, 0xF4 },
{ 0x31, 0x45, 0xF1, 0x53, 0x00, 0x32, 0x1F, 0xF2, 0x27, 0x00, 0x06, 0xF4 },
{ 0x13, 0x0C, 0xF2, 0x01, 0x00, 0x15, 0x2F, 0xF2, 0xB6, 0x00, 0x08, 0xF4 },
{ 0x11, 0x0C, 0xF2, 0x01, 0x00, 0x11, 0x1F, 0xF2, 0xB6, 0x00, 0x08, 0xF4 },
{ 0x11, 0x0A, 0xFE, 0x04, 0x00, 0x11, 0x1F, 0xF2, 0xBD, 0x00, 0x08, 0xF4 },
{ 0x16, 0x4D, 0xFA, 0x11, 0x00, 0xE1, 0x20, 0xF1, 0xF1, 0x00, 0x08, 0xF4 },
{ 0x16, 0x40, 0xBA, 0x11, 0x00, 0xF1, 0x20, 0x24, 0x31, 0x00, 0x08, 0xF4 },
{ 0x61, 0xA7, 0x72, 0x8E, 0x00, 0xE1, 0x9F, 0x50, 0x1A, 0x00, 0x02, 0xF4 },
{ 0x18, 0x4D, 0x32, 0x13, 0x00, 0xE1, 0x20, 0x51, 0xE3, 0x00, 0x08, 0xF4 },
{ 0x17, 0xC0, 0x12, 0x41, 0x00, 0x31, 0x9F, 0x13, 0x31, 0x00, 0x06, 0xF4 },
{ 0x03, 0x8F, 0xF5, 0x55, 0x00, 0x21, 0x9F, 0xF3, 0x33, 0x00, 0x00, 0xF4 },
{ 0x13, 0x4D, 0xFA, 0x11, 0x00, 0xE1, 0x20, 0xF1, 0xF1, 0x00, 0x08, 0xF4 },
{ 0x11, 0x43, 0x20, 0x15, 0x00, 0xF1, 0x20, 0x31, 0xF8, 0x00, 0x08, 0xF4 },
{ 0x11, 0x03, 0x82, 0x97, 0x00, 0xE4, 0x60, 0xF0, 0xF2, 0x00, 0x08, 0xF4 },
{ 0x05, 0x40, 0xD1, 0x53, 0x00, 0x14, 0x1F, 0x51, 0x71, 0x00, 0x06, 0xF4 },
{ 0xF1, 0x01, 0x77, 0x17, 0x00, 0x21, 0x1F, 0x81, 0x18, 0x00, 0x02, 0xF4 },
{ 0xF1, 0x18, 0x32, 0x11, 0x00, 0xE1, 0x1F, 0xF1, 0x13, 0x00, 0x00, 0xF4 },
{ 0x73, 0x48, 0xF1, 0x53, 0x00, 0x71, 0x1F, 0xF1, 0x06, 0x00, 0x08, 0xF4 },
{ 0x71, 0x8D, 0x71, 0x11, 0x00, 0x61, 0x5F, 0x72, 0x15, 0x00, 0x06, 0xF4 },
{ 0xD7, 0x4F, 0xF2, 0x61, 0x00, 0xD2, 0x1F, 0xF1, 0xB2, 0x00, 0x08, 0xF4 },
{ 0x01, 0x11, 0xF0, 0xFF, 0x00, 0x01, 0x1F, 0xF0, 0xF8, 0x00, 0x0A, 0xF4 },
{ 0x31, 0x8B, 0x41, 0x11, 0x00, 0x61, 0x1F, 0x22, 0x13, 0x00, 0x06, 0xF4 },
{ 0x71, 0x1C, 0x71, 0x03, 0x00, 0x21, 0x1F, 0x64, 0x07, 0x00, 0x0E, 0xF4 },
{ 0x31, 0x8B, 0x41, 0x11, 0x00, 0x61, 0x1F, 0x32, 0x15, 0x00, 0x02, 0xF4 },
{ 0x71, 0x1C, 0xFD, 0x13, 0x00, 0x21, 0x1F, 0xE7, 0xD6, 0x00, 0x0E, 0xF4 },
{ 0x71, 0x1C, 0x51, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x67, 0x00, 0x0E, 0xF4 },
{ 0x71, 0x1C, 0x51, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 },
{ 0x71, 0x1C, 0x54, 0x15, 0x00, 0x21, 0x1F, 0x53, 0x49, 0x00, 0x0E, 0xF4 },
{ 0x71, 0x56, 0x51, 0x03, 0x00, 0x61, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 },
{ 0x71, 0x1C, 0x51, 0x03, 0x00, 0x21, 0x1F, 0x54, 0x17, 0x00, 0x0E, 0xF4 },
{ 0x02, 0x29, 0xF5, 0x75, 0x00, 0x01, 0x9F, 0xF2, 0xF3, 0x00, 0x00, 0xF4 },
{ 0x02, 0x29, 0xF0, 0x75, 0x00, 0x01, 0x9F, 0xF4, 0x33, 0x00, 0x00, 0xF4 },
{ 0x01, 0x49, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 },
{ 0x01, 0x89, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 },
{ 0x02, 0x89, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 },
{ 0x02, 0x80, 0xF1, 0x53, 0x00, 0x11, 0x1F, 0xF1, 0x74, 0x00, 0x06, 0xF4 },
{ 0x01, 0x40, 0xF1, 0x53, 0x00, 0x08, 0x5F, 0xF1, 0x53, 0x00, 0x00, 0xF4 },
{ 0x21, 0x15, 0xD3, 0x2C, 0x00, 0x21, 0x9F, 0xC3, 0x2C, 0x00, 0x0A, 0xF4 },
{ 0x01, 0x18, 0xD4, 0xF2, 0x00, 0x21, 0x9F, 0xC4, 0x8A, 0x00, 0x0A, 0xF4 },
{ 0x01, 0x4E, 0xF0, 0x7B, 0x00, 0x11, 0x1F, 0xF4, 0xC8, 0x00, 0x04, 0xF4 },
{ 0x01, 0x44, 0xF0, 0xAB, 0x00, 0x11, 0x1F, 0xF3, 0xAB, 0x00, 0x04, 0xF4 },
{ 0x53, 0x0E, 0xF4, 0xC8, 0x00, 0x11, 0x1F, 0xF1, 0xBB, 0x00, 0x04, 0xF4 },
{ 0x53, 0x0B, 0xF2, 0xC8, 0x00, 0x11, 0x1F, 0xF2, 0xC5, 0x00, 0x04, 0xF4 },
{ 0x21, 0x15, 0xB4, 0x4C, 0x00, 0x21, 0x1F, 0x94, 0xAC, 0x00, 0x0A, 0xF4 },
{ 0x21, 0x15, 0x94, 0x1C, 0x00, 0x21, 0x1F, 0x64, 0xAC, 0x00, 0x0A, 0xF4 },
{ 0x22, 0x1B, 0x97, 0x89, 0x00, 0xA2, 0x1F, 0x70, 0x07, 0x00, 0x0A, 0xF4 },
{ 0x21, 0x19, 0x77, 0xBF, 0x00, 0xA1, 0x9F, 0x60, 0x2A, 0x00, 0x06, 0xF4 },
{ 0xA1, 0x13, 0xD6, 0xAF, 0x00, 0xE2, 0x9F, 0x60, 0x2A, 0x00, 0x02, 0xF4 },
{ 0xA2, 0x1D, 0x95, 0x24, 0x00, 0xE2, 0x9F, 0x60, 0x2A, 0x00, 0x02, 0xF4 },
{ 0x32, 0x9A, 0x51, 0x19, 0x00, 0x61, 0x9F, 0x60, 0x39, 0x00, 0x0C, 0xF4 },
{ 0xA4, 0x12, 0xF4, 0x30, 0x00, 0xE2, 0x9F, 0x60, 0x2A, 0x00, 0x02, 0xF4 },
{ 0x21, 0x16, 0x63, 0x0E, 0x00, 0x21, 0x1F, 0x63, 0x0E, 0x00, 0x0C, 0xF4 },
{ 0x31, 0x16, 0x63, 0x0A, 0x00, 0x21, 0x1F, 0x63, 0x0B, 0x00, 0x0C, 0xF4 },
{ 0x21, 0x1B, 0x63, 0x0A, 0x00, 0x21, 0x1F, 0x63, 0x0B, 0x00, 0x0C, 0xF4 },
{ 0x20, 0x1B, 0x63, 0x0A, 0x00, 0x21, 0x1F, 0x63, 0x0B, 0x00, 0x0C, 0xF4 },
{ 0x32, 0x1C, 0x82, 0x18, 0x00, 0x61, 0x9F, 0x60, 0x07, 0x00, 0x0C, 0xF4 },
{ 0x32, 0x18, 0x61, 0x14, 0x00, 0xE1, 0x9F, 0x72, 0x16, 0x00, 0x0C, 0xF4 },
{ 0x31, 0xC0, 0x77, 0x17, 0x00, 0x22, 0x1F, 0x6B, 0x09, 0x00, 0x02, 0xF4 },
{ 0x71, 0xC3, 0x8E, 0x17, 0x00, 0x22, 0x24, 0x8B, 0x0E, 0x00, 0x02, 0xF4 },
{ 0x70, 0x8D, 0x6E, 0x17, 0x00, 0x22, 0x1F, 0x6B, 0x0E, 0x00, 0x02, 0xF4 },
{ 0x24, 0x4F, 0xF2, 0x06, 0x00, 0x31, 0x1F, 0x52, 0x06, 0x00, 0x0E, 0xF4 },
{ 0x31, 0x1B, 0x64, 0x07, 0x00, 0x61, 0x1F, 0xD0, 0x67, 0x00, 0x0E, 0xF4 },
{ 0x31, 0x1B, 0x61, 0x06, 0x00, 0x61, 0x1F, 0xD2, 0x36, 0x00, 0x0C, 0xF4 },
{ 0x31, 0x1F, 0x31, 0x06, 0x00, 0x61, 0x1F, 0x50, 0x36, 0x00, 0x0C, 0xF4 },
{ 0x31, 0x1F, 0x41, 0x06, 0x00, 0x61, 0x1F, 0xA0, 0x36, 0x00, 0x0C, 0xF4 },
{ 0x21, 0x9A, 0x53, 0x56, 0x00, 0x21, 0x9F, 0xA0, 0x16, 0x00, 0x0E, 0xF4 },
{ 0x21, 0x9A, 0x53, 0x56, 0x00, 0x21, 0x9F, 0xA0, 0x16, 0x00, 0x0E, 0xF4 },
{ 0x61, 0x19, 0x53, 0x58, 0x00, 0x21, 0x1F, 0xA0, 0x18, 0x00, 0x0C, 0xF4 },
{ 0x61, 0x19, 0x73, 0x57, 0x00, 0x21, 0x1F, 0xA0, 0x17, 0x00, 0x0C, 0xF4 },
{ 0x21, 0x1B, 0x71, 0xA6, 0x00, 0x21, 0x1F, 0xA1, 0x96, 0x00, 0x0E, 0xF4 },
{ 0x85, 0x91, 0xF5, 0x44, 0x00, 0xA1, 0x1F, 0xF0, 0x45, 0x00, 0x06, 0xF4 },
{ 0x07, 0x51, 0xF5, 0x33, 0x00, 0x61, 0x1F, 0xF0, 0x25, 0x00, 0x06, 0xF4 },
{ 0x13, 0x8C, 0xFF, 0x21, 0x00, 0x11, 0x9F, 0xFF, 0x03, 0x00, 0x0E, 0xF4 },
{ 0x38, 0x8C, 0xF3, 0x0D, 0x00, 0xB1, 0x5F, 0xF5, 0x33, 0x00, 0x0E, 0xF4 },
{ 0x87, 0x91, 0xF5, 0x55, 0x00, 0x22, 0x1F, 0xF0, 0x54, 0x00, 0x06, 0xF4 },
{ 0xB6, 0x4A, 0xB6, 0x32, 0x00, 0x11, 0x2B, 0xD1, 0x31, 0x00, 0x0E, 0xF4 },
{ 0x04, 0x00, 0xFE, 0xF0, 0x00, 0xC2, 0x1F, 0xF6, 0xB5, 0x00, 0x0E, 0xF4 },
{ 0x05, 0x4E, 0xDA, 0x15, 0x00, 0x01, 0x9F, 0xF0, 0x13, 0x00, 0x0A, 0xF4 },
{ 0x31, 0x44, 0xF2, 0x9A, 0x00, 0x32, 0x1F, 0xF0, 0x27, 0x00, 0x06, 0xF4 },
{ 0xB0, 0xC4, 0xA4, 0x02, 0x00, 0xD7, 0x9F, 0x40, 0x42, 0x00, 0x00, 0xF4 },
{ 0xCA, 0x84, 0xF0, 0xF0, 0x00, 0xCF, 0x1F, 0x59, 0x62, 0x00, 0x0C, 0xF4 },
{ 0x30, 0x35, 0xF5, 0xF0, 0x00, 0x35, 0x1F, 0xF0, 0x9B, 0x00, 0x02, 0xF4 },
{ 0x63, 0x0F, 0xF4, 0x04, 0x02, 0x6F, 0x1F, 0xF0, 0x43, 0x00, 0x06, 0xF4 },
{ 0x07, 0x40, 0x09, 0x53, 0x00, 0x05, 0x1F, 0xF6, 0x94, 0x00, 0x0E, 0xF4 },
{ 0x09, 0x4E, 0xDA, 0x25, 0x00, 0x01, 0x1F, 0xF1, 0x15, 0x00, 0x0A, 0xF4 },
{ 0x04, 0x00, 0xF3, 0xA0, 0x02, 0x04, 0x1F, 0xF8, 0x46, 0x00, 0x0E, 0xF4 },
{ 0x07, 0x00, 0xF0, 0xF0, 0x00, 0x00, 0x1F, 0x5C, 0xDC, 0x00, 0x0E, 0xF4 },
{ 0x1F, 0x1E, 0xE5, 0x5B, 0x00, 0x0F, 0x1F, 0x5D, 0xFA, 0x00, 0x0E, 0xF4 },
{ 0x11, 0x8A, 0xF1, 0x11, 0x00, 0x01, 0x5F, 0xF1, 0xB3, 0x00, 0x06, 0xF4 },
{ 0x00, 0x40, 0xD1, 0x53, 0x00, 0x00, 0x1F, 0xF2, 0x56, 0x00, 0x0E, 0xF4 },
{ 0x32, 0x44, 0xF8, 0xFF, 0x00, 0x11, 0x1F, 0xF5, 0x7F, 0x00, 0x0E, 0xF4 },
{ 0x00, 0x40, 0x09, 0x53, 0x00, 0x02, 0x1F, 0xF7, 0x94, 0x00, 0x0E, 0xF4 },
{ 0x11, 0x86, 0xF2, 0xA8, 0x00, 0x01, 0x9F, 0xA0, 0xA8, 0x00, 0x08, 0xF4 },
{ 0x00, 0x50, 0xF2, 0x70, 0x00, 0x13, 0x1F, 0xF2, 0x72, 0x00, 0x0E, 0xF4 },
{ 0xF0, 0x00, 0x11, 0x11, 0x00, 0xE0, 0xDF, 0x11, 0x11, 0x00, 0x0E, 0xF4 }
};
// hardcoded, dumped from ADHOM.DRV
uint16 adlib_FrequencyLookUpTable[SHERLOCK_ADLIB_NOTES_COUNT] = {
0x0158, 0x016C, 0x0182, 0x0199, 0x01B1, 0x01CB, 0x01E6, 0x0203, 0x0222, 0x0242,
0x0265, 0x0289, 0x0558, 0x056C, 0x0582, 0x0599, 0x05B1, 0x05CB, 0x05E6, 0x0603,
0x0622, 0x0642, 0x0665, 0x0689, 0x0958, 0x096C, 0x0982, 0x0999, 0x09B1, 0x09CB,
0x09E6, 0x0A03, 0x0A22, 0x0A42, 0x0A65, 0x0A89, 0x0D58, 0x0D6C, 0x0D82, 0x0D99,
0x0DB1, 0x0DCB, 0x0DE6, 0x0E03, 0x0E22, 0x0E42, 0x0E65, 0x0E89, 0x1158, 0x116C,
0x1182, 0x1199, 0x11B1, 0x11CB, 0x11E6, 0x1203, 0x1222, 0x1242, 0x1265, 0x1289,
0x1558, 0x156C, 0x1582, 0x1599, 0x15B1, 0x15CB, 0x15E6, 0x1603, 0x1622, 0x1642,
0x1665, 0x1689, 0x1958, 0x196C, 0x1982, 0x1999, 0x19B1, 0x19CB, 0x19E6, 0x1A03,
0x1A22, 0x1A42, 0x1A65, 0x1A89, 0x1D58, 0x1D6C, 0x1D82, 0x1D99, 0x1DB1, 0x1DCB,
0x1DE6, 0x1E03, 0x1E22, 0x1E42, 0x1E65, 0x1E89
};
class MidiDriver_AdLib : public MidiDriver_Emulated {
public:
MidiDriver_AdLib(Audio::Mixer *mixer)
: MidiDriver_Emulated(mixer), _masterVolume(15), _rhythmKeyMap(0), _opl(0) {
memset(_voiceChannelMapping, 0, sizeof(_voiceChannelMapping));
}
virtual ~MidiDriver_AdLib() { }
// MidiDriver
int open();
void close();
void send(uint32 b);
MidiChannel *allocateChannel() { return NULL; }
MidiChannel *getPercussionChannel() { return NULL; }
// AudioStream
bool isStereo() const { return false; }
int getRate() const { return _mixer->getOutputRate(); }
int getPolyphony() const { return SHERLOCK_ADLIB_VOICES_COUNT; }
bool hasRhythmChannel() const { return false; }
// MidiDriver_Emulated
void generateSamples(int16 *buf, int len);
void setVolume(byte volume);
virtual uint32 property(int prop, uint32 param);
bool useRhythmChannel() const { return _rhythmKeyMap != NULL; }
void newMusicData(byte *musicData, int32 musicDataSize);
private:
struct adlib_ChannelEntry {
bool inUse;
const adlib_InstrumentEntry *currentInstrumentPtr;
byte currentNote;
byte currentA0hReg;
byte currentB0hReg;
adlib_ChannelEntry() : inUse(false), currentInstrumentPtr(NULL), currentNote(0),
currentA0hReg(0), currentB0hReg(0) { }
};
OPL::OPL *_opl;
int _masterVolume;
byte *_rhythmKeyMap;
// points to a MIDI channel for each of the new voice channels
byte _voiceChannelMapping[SHERLOCK_ADLIB_VOICES_COUNT];
// stores information about all FM voice channels
adlib_ChannelEntry _channels[SHERLOCK_ADLIB_VOICES_COUNT];
void programChange(byte channel, byte parameter);
void setRegister(int reg, int value);
void noteOn(byte channel, byte note, byte velocity);
void noteOff(byte channel, byte note);
void voiceOnOff(byte FMVoiceChannel, bool KeyOn, byte note, byte velocity);
};
#if USE_SCI_MIDI_PLAYER
class MidiPlayer_AdLib : public MidiPlayer {
public:
MidiPlayer_AdLib() : MidiPlayer() { _driver = new MidiDriver_AdLib(g_system->getMixer()); }
~MidiPlayer_AdLib() {
delete _driver;
_driver = 0;
}
int open();
void close();
byte getPlayId() const;
int getPolyphony() const { return SHERLOCK_ADLIB_VOICES_COUNT; }
bool hasRhythmChannel() const { return false; }
void setVolume(byte volume) { static_cast<MidiDriver_AdLib *>(_driver)->setVolume(volume); }
//int getLastChannel() const { return (static_cast<const MidiDriver_AdLib *>(_driver)->useRhythmChannel() ? 8 : 15); }
void newMusicData(byte *musicData, int32 musicDataSize) { static_cast<MidiDriver_AdLib *>(_driver)->newMusicData(musicData, musicDataSize); }
};
#endif
int MidiDriver_AdLib::open() {
int rate = _mixer->getOutputRate();
debug(3, "ADLIB: Starting driver");
_opl = OPL::Config::create(OPL::Config::kOpl2);
if (!_opl)
return -1;
_opl->init(rate);
setRegister(0xBD, 0);
setRegister(0x08, 0);
setRegister(0x01, 0x20);
MidiDriver_Emulated::open();
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO);
return 0;
}
void MidiDriver_AdLib::close() {
_mixer->stopHandle(_mixerSoundHandle);
delete _opl;
delete[] _rhythmKeyMap;
}
void MidiDriver_AdLib::setVolume(byte volume) {
_masterVolume = volume;
//renewNotes(-1, true);
}
// Called when a music track got loaded into memory
void MidiDriver_AdLib::newMusicData(byte *musicData, int32 musicDataSize) {
assert(musicDataSize >= 0x7F);
// MIDI Channel <-> FM Voice Channel mapping at offset 0x22 of music data
memcpy(&_voiceChannelMapping, musicData + 0x22, 9);
// reset OPL here?
// reset current channel data
memset(&_channels, 0, sizeof(_channels));
}
// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php
void MidiDriver_AdLib::send(uint32 b) {
byte command = b & 0xf0;
byte channel = b & 0xf;
byte op1 = (b >> 8) & 0xff;
byte op2 = (b >> 16) & 0xff;
switch (command) {
case 0x80:
noteOff(channel, op1);
break;
case 0x90:
noteOn(channel, op1, op2);
break;
case 0xb0: // Control change
// Doesn't seem to be implemented in the Sherlock Holmes adlib driver
break;
case 0xc0: // Program Change
programChange(channel, op1);
break;
case 0xa0: // Polyphonic key pressure (aftertouch)
case 0xd0: // Channel pressure (aftertouch)
// Aftertouch doesn't seem to be implemented in the Sherlock Holmes adlib driver
break;
case 0xe0:
// TODO: Implement this, occurs right in the intro, second scene
warning("pitch bend change");
break;
case 0xf0: // SysEx
warning("SysEx: %lx", b);
break;
default:
warning("ADLIB: Unknown event %02x", command);
}
}
void MidiDriver_AdLib::generateSamples(int16 *data, int len) {
_opl->readBuffer(data, len);
}
void MidiDriver_AdLib::noteOn(byte MIDIchannel, byte note, byte velocity) {
if (velocity == 0)
return noteOff(MIDIchannel, note);
if (MIDIchannel != 9) {
// Not Percussion
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) {
if (!_channels[FMvoiceChannel].inUse) {
_channels[FMvoiceChannel].inUse = true;
_channels[FMvoiceChannel].currentNote = note;
voiceOnOff(FMvoiceChannel, true, note, velocity);
return;
}
}
}
}
warning("MIDI channel not mapped/all FM voice channels busy %d", MIDIchannel);
}
void MidiDriver_AdLib::noteOff(byte MIDIchannel, byte note) {
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) {
if (_channels[FMvoiceChannel].currentNote == note) {
_channels[FMvoiceChannel].inUse = false;
voiceOnOff(FMvoiceChannel, false, note, 0);
return;
}
}
}
}
void MidiDriver_AdLib::voiceOnOff(byte FMvoiceChannel, bool keyOn, byte note, byte velocity) {
byte frequencyOffset = 0;
uint16 frequency = 0;
byte op2RegAdjust = 0;
byte regValue40h = 0;
byte regValueA0h = 0;
byte regValueB0h = 0;
// Look up frequency
if (_channels[FMvoiceChannel].currentInstrumentPtr) {
frequencyOffset = note + _channels[FMvoiceChannel].currentInstrumentPtr->frequencyAdjust;
} else {
frequencyOffset = note;
}
if (frequencyOffset >= SHERLOCK_ADLIB_NOTES_COUNT) {
error("bad note!");
}
frequency = adlib_FrequencyLookUpTable[frequencyOffset];
if (keyOn) {
// adjust register 40h
if (_channels[FMvoiceChannel].currentInstrumentPtr) {
regValue40h = _channels[FMvoiceChannel].currentInstrumentPtr->reg40op2;
}
regValue40h = regValue40h - (velocity >> 3);
op2RegAdjust = adlib_Operator2Register[FMvoiceChannel];
setRegister(0x40 + op2RegAdjust, regValue40h);
}
regValueA0h = frequency & 0xFF;
regValueB0h = frequency >> 8;
if (keyOn) {
regValueB0h |= 0x20; // set Key-On flag
}
setRegister(0xA0 + FMvoiceChannel, regValueA0h);
setRegister(0xB0 + FMvoiceChannel, regValueB0h);
_channels[FMvoiceChannel].currentA0hReg = regValueA0h;
_channels[FMvoiceChannel].currentB0hReg = regValueB0h;
}
void MidiDriver_AdLib::programChange(byte MIDIchannel, byte op1) {
const adlib_InstrumentEntry *instrumentPtr;
byte op1Reg = 0;
byte op2Reg = 0;
// setup instrument
instrumentPtr = &adlib_instrumentTable[op1];
//warning("program change for MIDI channel %d, instrument id %d", MIDIchannel, op1);
for (byte FMvoiceChannel = 0; FMvoiceChannel < SHERLOCK_ADLIB_VOICES_COUNT; FMvoiceChannel++) {
if (_voiceChannelMapping[FMvoiceChannel] == MIDIchannel) {
op1Reg = adlib_Operator1Register[FMvoiceChannel];
op2Reg = adlib_Operator2Register[FMvoiceChannel];
setRegister(0x20 + op1Reg, instrumentPtr->reg20op1);
setRegister(0x40 + op1Reg, instrumentPtr->reg40op1);
setRegister(0x60 + op1Reg, instrumentPtr->reg60op1);
setRegister(0x80 + op1Reg, instrumentPtr->reg80op1);
setRegister(0xE0 + op1Reg, instrumentPtr->regE0op1);
setRegister(0x20 + op2Reg, instrumentPtr->reg20op2);
setRegister(0x40 + op2Reg, instrumentPtr->reg40op2);
setRegister(0x60 + op2Reg, instrumentPtr->reg60op2);
setRegister(0x80 + op2Reg, instrumentPtr->reg80op2);
setRegister(0xE0 + op2Reg, instrumentPtr->regE0op2);
setRegister(0xC0 + FMvoiceChannel, instrumentPtr->regC0);
// Remember instrument
_channels[FMvoiceChannel].currentInstrumentPtr = instrumentPtr;
}
}
}
void MidiDriver_AdLib::setRegister(int reg, int value) {
_opl->write(0x220, reg);
_opl->write(0x221, value);
}
uint32 MidiDriver_AdLib::property(int prop, uint32 param) {
#if 0
switch(prop) {
case MIDI_PROP_MASTER_VOLUME:
if (param != 0xffff)
_masterVolume = param;
return _masterVolume;
default:
break;
}
#endif
return 0;
}
#if USE_SCI_MIDI_PLAYER
int MidiPlayer_AdLib::open() {
return static_cast<MidiDriver_AdLib *>(_driver)->open();
}
void MidiPlayer_AdLib::close() {
if (_driver) {
_driver->close();
}
}
byte MidiPlayer_AdLib::getPlayId() const {
return 0x00;
}
MidiPlayer *MidiPlayer_AdLib_create() {
return new MidiPlayer_AdLib();
}
void MidiPlayer_AdLib_newMusicData(MidiPlayer *driver, byte *musicData, int32 musicDataSize) {
static_cast<MidiPlayer_AdLib *>(driver)->newMusicData(musicData, musicDataSize);
}
#endif
MidiDriver *MidiDriver_AdLib_create() {
return new MidiDriver_AdLib(g_system->getMixer());
}
void MidiDriver_AdLib_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize) {
static_cast<MidiDriver_AdLib *>(driver)->newMusicData(musicData, musicDataSize);
}
} // End of namespace Sci

View file

@ -0,0 +1,91 @@
/* 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.
*
*/
#ifndef SHERLOCK_SOFTSEQ_MIDIDRIVER_H
#define SHERLOCK_SOFTSEQ_MIDIDRIVER_H
#include "sherlock/sherlock.h"
//#include "audio/mididrv.h"
#include "common/error.h"
namespace Sherlock {
#define USE_SCI_MIDIPLAYER 1
#if USE_SCI_MIDIPLAYER
enum {
MIDI_CHANNELS = 16,
MIDI_PROP_MASTER_VOLUME = 0
};
#define MIDI_RHYTHM_CHANNEL 9
class MidiPlayer : public MidiDriver_BASE {
protected:
MidiDriver *_driver;
int8 _reverb;
public:
MidiPlayer() : _driver(0), _reverb(-1) { }
virtual int open() { return _driver->open(); }
virtual void close() { _driver->close(); }
virtual void send(uint32 b) { _driver->send(b); }
virtual uint32 getBaseTempo() { return _driver->getBaseTempo(); }
virtual bool hasRhythmChannel() const = 0;
virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _driver->setTimerCallback(timer_param, timer_proc); }
virtual byte getPlayId() const = 0;
virtual int getPolyphony() const = 0;
virtual int getFirstChannel() const { return 0; }
//virtual int getLastChannel() const { return 15; }
virtual void setVolume(byte volume) {
if(_driver)
_driver->property(MIDI_PROP_MASTER_VOLUME, volume);
}
virtual int getVolume() {
return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0;
}
// Returns the current reverb, or -1 when no reverb is active
int8 getReverb() const { return _reverb; }
// Sets the current reverb, used mainly in MT-32
virtual void setReverb(int8 reverb) { _reverb = reverb; }
// Special stuff for Sherlock Holmes
// virtual void newMusicData(byte *musicData, int32 musicDataSize);
//protected:
};
extern MidiPlayer *MidiPlayer_AdLib_create();
extern void MidiPlayer_AdLib_newMusicData(MidiPlayer *driver, byte *musicData, int32 musicDataSize);
#endif
extern MidiDriver *MidiDriver_AdLib_create();
extern void MidiDriver_AdLib_newMusicData(MidiDriver *driver, byte *musicData, int32 musicDataSize);
} // End of namespace Sci
#endif // SHERLOCK_SOFTSEQ_MIDIDRIVER_H