mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-20 19:21:46 +00:00
CINE: Implement MT-32 output driver.
The driver is based on Future Wars. I do not own Operation Stealth, thus I do not know whether it works fine for it.
This commit is contained in:
parent
5ac244313f
commit
c48af08a0b
@ -65,7 +65,7 @@ class CineMetaEngine : public AdvancedMetaEngine {
|
||||
public:
|
||||
CineMetaEngine() : AdvancedMetaEngine(Cine::gameDescriptions, sizeof(Cine::CINEGameDescription), cineGames) {
|
||||
_singleid = "cine";
|
||||
_guioptions = GUIO2(GUIO_NOSPEECH, GUIO_NOMIDI);
|
||||
_guioptions = GUIO1(GUIO_NOSPEECH);
|
||||
}
|
||||
|
||||
virtual GameDescriptor findGame(const char *gameid) const {
|
||||
|
@ -122,7 +122,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_FW,
|
||||
0,
|
||||
@ -136,7 +136,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::EN_USA,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_FW,
|
||||
0,
|
||||
@ -150,7 +150,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_FW,
|
||||
GF_ALT_FONT,
|
||||
@ -164,7 +164,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_FW,
|
||||
0,
|
||||
@ -178,7 +178,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_FW,
|
||||
0,
|
||||
@ -192,7 +192,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::IT_ITA,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_FW,
|
||||
0,
|
||||
@ -210,7 +210,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_DEMO,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_FW,
|
||||
0,
|
||||
@ -224,7 +224,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformAtariST,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_FW,
|
||||
0,
|
||||
@ -238,7 +238,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformAtariST,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_FW,
|
||||
0,
|
||||
@ -384,7 +384,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::EN_GRB,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_OS,
|
||||
0,
|
||||
@ -398,7 +398,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::EN_GRB,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_OS,
|
||||
0,
|
||||
@ -412,7 +412,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::EN_USA,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_OS,
|
||||
0,
|
||||
@ -426,7 +426,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::DE_DEU,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_OS,
|
||||
0,
|
||||
@ -440,7 +440,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::ES_ESP,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_OS,
|
||||
0,
|
||||
@ -454,7 +454,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_OS,
|
||||
0,
|
||||
@ -468,7 +468,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::EN_GRB,
|
||||
Common::kPlatformAmiga,
|
||||
ADGF_DEMO,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_OS,
|
||||
GF_DEMO,
|
||||
@ -482,7 +482,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::EN_GRB,
|
||||
Common::kPlatformAtariST,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_OS,
|
||||
0,
|
||||
@ -496,7 +496,7 @@ static const CINEGameDescription gameDescriptions[] = {
|
||||
Common::FR_FRA,
|
||||
Common::kPlatformAtariST,
|
||||
ADGF_NO_FLAGS,
|
||||
GUIO0()
|
||||
GUIO1(GUIO_NOMIDI)
|
||||
},
|
||||
GType_OS,
|
||||
0,
|
||||
|
@ -25,12 +25,14 @@
|
||||
#include "common/memstream.h"
|
||||
#include "common/system.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/timer.h"
|
||||
|
||||
#include "cine/cine.h"
|
||||
#include "cine/sound.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/fmopl.h"
|
||||
#include "audio/mididrv.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/mods/soundfx.h"
|
||||
|
||||
@ -48,14 +50,13 @@ public:
|
||||
virtual void playSample(const byte *data, int size, int channel, int volume) = 0;
|
||||
virtual void stopAll() = 0;
|
||||
virtual const char *getInstrumentExtension() const { return ""; }
|
||||
virtual void notifyInstrumentLoad(const byte *data, int size, int channel) {}
|
||||
|
||||
void setUpdateCallback(UpdateCallback upCb, void *ref);
|
||||
virtual void setUpdateCallback(UpdateCallback upCb, void *ref) = 0;
|
||||
void resetChannel(int channel);
|
||||
void findNote(int freq, int *note, int *oct) const;
|
||||
|
||||
protected:
|
||||
UpdateCallback _upCb;
|
||||
void *_upRef;
|
||||
|
||||
static const int _noteTable[];
|
||||
static const int _noteTableCount;
|
||||
@ -104,6 +105,7 @@ public:
|
||||
virtual ~AdLibSoundDriver();
|
||||
|
||||
// PCSoundDriver interface
|
||||
virtual void setUpdateCallback(UpdateCallback upCb, void *ref);
|
||||
virtual void setupChannel(int channel, const byte *data, int instrument, int volume);
|
||||
virtual void stopChannel(int channel);
|
||||
virtual void stopAll();
|
||||
@ -121,6 +123,9 @@ public:
|
||||
virtual void loadInstrument(const byte *data, AdLibSoundInstrument *asi) = 0;
|
||||
|
||||
protected:
|
||||
UpdateCallback _upCb;
|
||||
void *_upRef;
|
||||
|
||||
FM_OPL *_opl;
|
||||
int _sampleRate;
|
||||
Audio::Mixer *_mixer;
|
||||
@ -177,6 +182,29 @@ public:
|
||||
virtual void playSample(const byte *data, int size, int channel, int volume);
|
||||
};
|
||||
|
||||
// (Future Wars) MIDI driver
|
||||
class MidiSoundDriverH32 : public PCSoundDriver {
|
||||
public:
|
||||
MidiSoundDriverH32(MidiDriver *output);
|
||||
~MidiSoundDriverH32();
|
||||
|
||||
virtual void setUpdateCallback(UpdateCallback upCb, void *ref);
|
||||
virtual void setupChannel(int channel, const byte *data, int instrument, int volume);
|
||||
virtual void setChannelFrequency(int channel, int frequency);
|
||||
virtual void stopChannel(int channel);
|
||||
virtual void playSample(const byte *data, int size, int channel, int volume);
|
||||
virtual void stopAll() {}
|
||||
virtual const char *getInstrumentExtension() const { return ".H32"; }
|
||||
virtual void notifyInstrumentLoad(const byte *data, int size, int channel);
|
||||
|
||||
private:
|
||||
MidiDriver *_output;
|
||||
UpdateCallback _callback;
|
||||
|
||||
void writeInstrument(int offset, const byte *data, int size);
|
||||
void selectInstrument(int channel, int unk, int instrument, int volume);
|
||||
};
|
||||
|
||||
class PCSoundFxPlayer {
|
||||
public:
|
||||
|
||||
@ -216,11 +244,6 @@ private:
|
||||
};
|
||||
|
||||
|
||||
void PCSoundDriver::setUpdateCallback(UpdateCallback upCb, void *ref) {
|
||||
_upCb = upCb;
|
||||
_upRef = ref;
|
||||
}
|
||||
|
||||
void PCSoundDriver::findNote(int freq, int *note, int *oct) const {
|
||||
*note = _noteTableCount - 1;
|
||||
for (int i = 0; i < _noteTableCount; ++i) {
|
||||
@ -252,6 +275,11 @@ AdLibSoundDriver::~AdLibSoundDriver() {
|
||||
OPLDestroy(_opl);
|
||||
}
|
||||
|
||||
void AdLibSoundDriver::setUpdateCallback(UpdateCallback upCb, void *ref) {
|
||||
_upCb = upCb;
|
||||
_upRef = ref;
|
||||
}
|
||||
|
||||
void AdLibSoundDriver::setupChannel(int channel, const byte *data, int instrument, int volume) {
|
||||
assert(channel < 4);
|
||||
if (data) {
|
||||
@ -564,6 +592,133 @@ void AdLibSoundDriverADL::playSample(const byte *data, int size, int channel, in
|
||||
}
|
||||
}
|
||||
|
||||
MidiSoundDriverH32::MidiSoundDriverH32(MidiDriver *output)
|
||||
: _output(output), _callback(0) {
|
||||
}
|
||||
|
||||
MidiSoundDriverH32::~MidiSoundDriverH32() {
|
||||
if (_callback)
|
||||
g_system->getTimerManager()->removeTimerProc(_callback);
|
||||
|
||||
_output->close();
|
||||
delete _output;
|
||||
}
|
||||
|
||||
void MidiSoundDriverH32::setUpdateCallback(UpdateCallback upCb, void *ref) {
|
||||
Common::TimerManager *timer = g_system->getTimerManager();
|
||||
assert(timer);
|
||||
|
||||
if (_callback)
|
||||
timer->removeTimerProc(_callback);
|
||||
|
||||
_callback = upCb;
|
||||
if (_callback)
|
||||
timer->installTimerProc(_callback, 1000000 / 50, ref, "MidiSoundDriverH32");
|
||||
}
|
||||
|
||||
void MidiSoundDriverH32::setupChannel(int channel, const byte *data, int instrument, int volume) {
|
||||
if (volume < 0 || volume > 100)
|
||||
volume = 0;
|
||||
|
||||
if (!data)
|
||||
selectInstrument(channel, 0, 0, volume);
|
||||
else if (data[0] < 0x80)
|
||||
selectInstrument(channel, data[0] / 0x40, data[0] % 0x40, volume);
|
||||
else
|
||||
selectInstrument(channel, 2, instrument, volume);
|
||||
}
|
||||
|
||||
void MidiSoundDriverH32::setChannelFrequency(int channel, int frequency) {
|
||||
int note, oct;
|
||||
findNote(frequency, ¬e, &oct);
|
||||
note %= 12;
|
||||
note = oct * 12 + note + 12;
|
||||
|
||||
_output->send(0x91 + channel, note, 0x7F);
|
||||
}
|
||||
|
||||
void MidiSoundDriverH32::stopChannel(int channel) {
|
||||
_output->send(0xB1 + channel, 0x7B, 0x00);
|
||||
}
|
||||
|
||||
void MidiSoundDriverH32::playSample(const byte *data, int size, int channel, int volume) {
|
||||
stopChannel(channel);
|
||||
|
||||
volume = volume * 8 / 5;
|
||||
|
||||
if (data[0] < 0x80) {
|
||||
selectInstrument(channel, data[0] / 0x40, data[0] % 0x40, volume);
|
||||
} else {
|
||||
writeInstrument(channel * 512 + 0x80000, data + 1, 256);
|
||||
selectInstrument(channel, 2, channel, volume);
|
||||
}
|
||||
|
||||
_output->send(0x91 + channel, 12, 0x7F);
|
||||
}
|
||||
|
||||
void MidiSoundDriverH32::notifyInstrumentLoad(const byte *data, int size, int channel) {
|
||||
if (data[0] < 0x80 || data[0] > 0xC0)
|
||||
return;
|
||||
|
||||
writeInstrument(channel * 512 + 0x80000, data + 1, size - 1);
|
||||
}
|
||||
|
||||
void MidiSoundDriverH32::writeInstrument(int offset, const byte *data, int size) {
|
||||
byte sysEx[254];
|
||||
|
||||
sysEx[0] = 0x41;
|
||||
sysEx[1] = 0x10;
|
||||
sysEx[2] = 0x16;
|
||||
sysEx[3] = 0x12;
|
||||
sysEx[4] = (offset >> 16) & 0xFF;
|
||||
sysEx[5] = (offset >> 8) & 0xFF;
|
||||
sysEx[6] = (offset >> 0) & 0xFF;
|
||||
int copySize = MIN(246, size);
|
||||
memcpy(&sysEx[7], data, copySize);
|
||||
|
||||
byte checkSum = 0;
|
||||
for (int i = 0; i < copySize + 3; ++i)
|
||||
checkSum += sysEx[4 + i];
|
||||
sysEx[7 + copySize] = 0x80 - (checkSum & 0x7F);
|
||||
|
||||
_output->sysEx(sysEx, copySize + 8);
|
||||
}
|
||||
|
||||
void MidiSoundDriverH32::selectInstrument(int channel, int unk, int instrument, int volume) {
|
||||
const int offset = channel * 16 + 0x30000;
|
||||
|
||||
byte sysEx[24] = {
|
||||
0x41, 0x10, 0x16, 0x12,
|
||||
0x00, 0x00, 0x00, // offset
|
||||
0x00, // unk
|
||||
0x00, // instrument
|
||||
0x18, 0x32, 0x0C, 0x03, 0x01, 0x00,
|
||||
0x00, // volume
|
||||
0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00 // checksum
|
||||
};
|
||||
|
||||
|
||||
sysEx[4] = (offset >> 16) & 0xFF;
|
||||
sysEx[5] = (offset >> 8) & 0xFF;
|
||||
sysEx[6] = (offset >> 0) & 0xFF;
|
||||
|
||||
sysEx[7] = unk;
|
||||
|
||||
sysEx[8] = instrument;
|
||||
|
||||
sysEx[15] = volume;
|
||||
|
||||
byte checkSum = 0;
|
||||
|
||||
for (int i = 4; i < 23; ++i)
|
||||
checkSum += sysEx[i];
|
||||
|
||||
sysEx[23] = 0x80 - (checkSum & 0x7F);
|
||||
|
||||
_output->sysEx(sysEx, 24);
|
||||
}
|
||||
|
||||
PCSoundFxPlayer::PCSoundFxPlayer(PCSoundDriver *driver)
|
||||
: _playing(false), _driver(driver) {
|
||||
memset(_instrumentsData, 0, sizeof(_instrumentsData));
|
||||
@ -608,9 +763,12 @@ bool PCSoundFxPlayer::load(const char *song) {
|
||||
*dot = '\0';
|
||||
}
|
||||
strcat(instrument, _driver->getInstrumentExtension());
|
||||
_instrumentsData[i] = readBundleSoundFile(instrument);
|
||||
uint32 instrumentSize;
|
||||
_instrumentsData[i] = readBundleSoundFile(instrument, &instrumentSize);
|
||||
if (!_instrumentsData[i]) {
|
||||
warning("Unable to load soundfx instrument '%s'", instrument);
|
||||
} else {
|
||||
_driver->notifyInstrumentLoad(_instrumentsData[i], instrumentSize, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -718,11 +876,27 @@ void PCSoundFxPlayer::unload() {
|
||||
|
||||
PCSound::PCSound(Audio::Mixer *mixer, CineEngine *vm)
|
||||
: Sound(mixer, vm) {
|
||||
if (_vm->getGameType() == GType_FW) {
|
||||
_soundDriver = new AdLibSoundDriverINS(_mixer);
|
||||
} else {
|
||||
_soundDriver = new AdLibSoundDriverADL(_mixer);
|
||||
|
||||
const MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB);
|
||||
const MusicType musicType = MidiDriver::getMusicType(dev);
|
||||
if (musicType == MT_MT32 || musicType == MT_GM) {
|
||||
MidiDriver *driver = MidiDriver::createMidi(dev);
|
||||
if (driver && driver->open() == 0) {
|
||||
driver->sendMT32Reset();
|
||||
_soundDriver = new MidiSoundDriverH32(driver);
|
||||
} else {
|
||||
warning("Could not create MIDI output falling back to AdLib");
|
||||
}
|
||||
}
|
||||
|
||||
if (!_soundDriver) {
|
||||
if (_vm->getGameType() == GType_FW) {
|
||||
_soundDriver = new AdLibSoundDriverINS(_mixer);
|
||||
} else {
|
||||
_soundDriver = new AdLibSoundDriverADL(_mixer);
|
||||
}
|
||||
}
|
||||
|
||||
_player = new PCSoundFxPlayer(_soundDriver);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user