SCUMM: (IMS) - minor restructuring and cleanup
Currently, Roland MT-32 sound is broken for SAMNMAX. Just try the intro song with shut off speech. It is very obvious that it plays with a quite reduced number of channels compared to the original interpreter. Now, due to the not-so-helpful code design (much of the iMuse code has been drawn into the common code) it has become increasingly difficult to fix Midi related thing in iMuse. I have added more and more crude hacks over time. SAMNMAX requires more elaborate channel allocation. To make it happen I have added driver wrappers for Midi to the iMuse code. Other than that, I have done only minor cleanup here. Actually, I would have liked to withdraw much more of the iMuse code from the common code and move it to SCUMM (basically all the MidiChannel stuff which is exclusively used by iMuse. But it turns out that it is so thoroughly intertwined (the major blocker here being the AdLib driver) that it requires more thought and effort and would just distract me from fixing the SAMNMAX sound.
This commit is contained in:
parent
be7e6731a9
commit
99dd6cb248
20 changed files with 606 additions and 375 deletions
|
@ -942,7 +942,6 @@ public:
|
|||
uint32 getBaseTempo() override { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
|
||||
|
||||
void setPitchBendRange(byte channel, uint range) override;
|
||||
void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) override;
|
||||
|
||||
MidiChannel *allocateChannel() override;
|
||||
MidiChannel *getPercussionChannel() override { return &_percussion; } // Percussion partially supported
|
||||
|
@ -1585,10 +1584,6 @@ void MidiDriver_ADLIB::setPitchBendRange(byte channel, uint range) {
|
|||
}
|
||||
}
|
||||
|
||||
void MidiDriver_ADLIB::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
|
||||
_parts[channel].sysEx_customInstrument(type, instr);
|
||||
}
|
||||
|
||||
MidiChannel *MidiDriver_ADLIB::allocateChannel() {
|
||||
AdLibPart *part;
|
||||
uint i;
|
||||
|
|
|
@ -499,8 +499,6 @@ public:
|
|||
*/
|
||||
void sendGMReset();
|
||||
|
||||
virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) { }
|
||||
|
||||
// Timing functions - MidiDriver now operates timers
|
||||
virtual void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) = 0;
|
||||
|
||||
|
|
|
@ -71,9 +71,6 @@ void MidiChannel_MPU401::pitchBendFactor(byte value) {
|
|||
_owner->setPitchBendRange(_channel, value);
|
||||
}
|
||||
|
||||
void MidiChannel_MPU401::sysEx_customInstrument(uint32 type, const byte *instr) {
|
||||
_owner->sysEx_customInstrument(_channel, type, instr);
|
||||
}
|
||||
|
||||
const char *MidiDriver::getErrorName(int error_code) {
|
||||
static const char *const midi_errors[] = {
|
||||
|
|
|
@ -55,7 +55,7 @@ public:
|
|||
virtual void pitchBendFactor(byte value);
|
||||
|
||||
// SysEx messages
|
||||
virtual void sysEx_customInstrument(uint32 type, const byte *instr);
|
||||
virtual void sysEx_customInstrument(uint32 type, const byte *instr) {}
|
||||
|
||||
// Only to be called by the owner
|
||||
void init(MidiDriver *owner, byte channel);
|
||||
|
|
|
@ -19,15 +19,17 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "engines/scumm/imuse/drivers/fmtowns.h"
|
||||
#include "scumm/imuse/drivers/fmtowns.h"
|
||||
#include "audio/softsynth/fmtowns_pc98/towns_pc98_fmsynth.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/system.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class TownsMidiOutputChannel {
|
||||
friend class TownsMidiInputChannel;
|
||||
public:
|
||||
TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanId);
|
||||
TownsMidiOutputChannel(IMuseDriver_FMTowns *driver, int chanId);
|
||||
~TownsMidiOutputChannel();
|
||||
|
||||
void noteOn(uint8 msb, uint16 lsb);
|
||||
|
@ -103,7 +105,7 @@ private:
|
|||
uint16 _freq;
|
||||
int16 _freqAdjust;
|
||||
|
||||
MidiDriver_TOWNS *_driver;
|
||||
IMuseDriver_FMTowns *_driver;
|
||||
|
||||
static const uint8 _chanMap[];
|
||||
static const uint8 _chanMap2[];
|
||||
|
@ -116,11 +118,12 @@ private:
|
|||
class TownsMidiInputChannel : public MidiChannel {
|
||||
friend class TownsMidiOutputChannel;
|
||||
public:
|
||||
TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex);
|
||||
TownsMidiInputChannel(IMuseDriver_FMTowns *driver, int16 number);
|
||||
~TownsMidiInputChannel() override;
|
||||
|
||||
MidiDriver *device() override { return _driver; }
|
||||
byte getNumber() override { return _chanIndex; }
|
||||
byte getNumber() override { return _number; }
|
||||
|
||||
bool allocate();
|
||||
void release() override;
|
||||
|
||||
|
@ -147,8 +150,8 @@ private:
|
|||
|
||||
TownsMidiOutputChannel *_out;
|
||||
|
||||
const byte _number;
|
||||
uint8 *_instrument;
|
||||
uint8 _chanIndex;
|
||||
uint8 _priority;
|
||||
uint8 _tl;
|
||||
int8 _transpose;
|
||||
|
@ -161,7 +164,7 @@ private:
|
|||
|
||||
bool _allocated;
|
||||
|
||||
MidiDriver_TOWNS *_driver;
|
||||
IMuseDriver_FMTowns *_driver;
|
||||
|
||||
static const uint8 _programAdjustLevel[];
|
||||
};
|
||||
|
@ -207,7 +210,7 @@ uint8 TownsMidiChanState::get(uint8 type) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
TownsMidiOutputChannel::TownsMidiOutputChannel(MidiDriver_TOWNS *driver, int chanIndex) : _driver(driver), _chan(chanIndex),
|
||||
TownsMidiOutputChannel::TownsMidiOutputChannel(IMuseDriver_FMTowns *driver, int number) : _driver(driver), _chan(number),
|
||||
_in(nullptr), _prev(nullptr), _next(nullptr), _adjustModTl(0), _operator2Tl(0), _note(0), _operator1Tl(0), _sustainNoteOff(0), _duration(0), _freq(0), _freqAdjust(0) {
|
||||
_effectEnvelopes = new EffectEnvelope[2]();
|
||||
_effectDefs = new EffectDef[2]();
|
||||
|
@ -651,7 +654,7 @@ const uint16 TownsMidiOutputChannel::_freqLSB[] = {
|
|||
0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B, 0x055B
|
||||
};
|
||||
|
||||
TownsMidiInputChannel::TownsMidiInputChannel(MidiDriver_TOWNS *driver, int chanIndex) : MidiChannel(), _driver(driver), _out(nullptr), _chanIndex(chanIndex),
|
||||
TownsMidiInputChannel::TownsMidiInputChannel(IMuseDriver_FMTowns *driver, int16 number) : MidiChannel(), _driver(driver), _number(number), _out(nullptr),
|
||||
_priority(0), _tl(0), _transpose(0), _pitchBendFactor(0), _pitchBend(0), _sustain(0), _freqLSB(0), _detune(0), _modWheel(0), _allocated(false) {
|
||||
_instrument = new uint8[30]();
|
||||
}
|
||||
|
@ -672,7 +675,7 @@ void TownsMidiInputChannel::release() {
|
|||
}
|
||||
|
||||
void TownsMidiInputChannel::send(uint32 b) {
|
||||
_driver->send(b | _chanIndex);
|
||||
device()->send(b | _number);
|
||||
}
|
||||
|
||||
void TownsMidiInputChannel::noteOff(byte note) {
|
||||
|
@ -836,7 +839,7 @@ const uint8 TownsMidiInputChannel::_programAdjustLevel[] = {
|
|||
0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F
|
||||
};
|
||||
|
||||
MidiDriver_TOWNS::MidiDriver_TOWNS(Audio::Mixer *mixer) : _timerProc(nullptr), _timerProcPara(nullptr), _channels(nullptr), _out(nullptr),
|
||||
IMuseDriver_FMTowns::IMuseDriver_FMTowns(Audio::Mixer *mixer) : _timerProc(nullptr), _timerProcPara(nullptr), _channels(nullptr), _out(nullptr),
|
||||
_baseTempo(10080), _chanState(nullptr), _operatorLevelTable(nullptr), _tickCounter(0), _rand(1), _allocCurPos(0), _isOpen(false) {
|
||||
_intf = new TownsAudioInterface(mixer, this, true);
|
||||
|
||||
|
@ -859,7 +862,7 @@ MidiDriver_TOWNS::MidiDriver_TOWNS(Audio::Mixer *mixer) : _timerProc(nullptr), _
|
|||
_operatorLevelTable[i << 5] = 0;
|
||||
}
|
||||
|
||||
MidiDriver_TOWNS::~MidiDriver_TOWNS() {
|
||||
IMuseDriver_FMTowns::~IMuseDriver_FMTowns() {
|
||||
close();
|
||||
delete _intf;
|
||||
|
||||
|
@ -883,7 +886,7 @@ MidiDriver_TOWNS::~MidiDriver_TOWNS() {
|
|||
_operatorLevelTable = nullptr;
|
||||
}
|
||||
|
||||
int MidiDriver_TOWNS::open() {
|
||||
int IMuseDriver_FMTowns::open() {
|
||||
if (_isOpen)
|
||||
return MERR_ALREADY_OPEN;
|
||||
|
||||
|
@ -906,7 +909,7 @@ int MidiDriver_TOWNS::open() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void MidiDriver_TOWNS::close() {
|
||||
void IMuseDriver_FMTowns::close() {
|
||||
if (!_isOpen)
|
||||
return;
|
||||
|
||||
|
@ -916,7 +919,7 @@ void MidiDriver_TOWNS::close() {
|
|||
g_system->delayMillis(20);
|
||||
}
|
||||
|
||||
void MidiDriver_TOWNS::send(uint32 b) {
|
||||
void IMuseDriver_FMTowns::send(uint32 b) {
|
||||
if (!_isOpen)
|
||||
return;
|
||||
|
||||
|
@ -954,16 +957,16 @@ void MidiDriver_TOWNS::send(uint32 b) {
|
|||
}
|
||||
}
|
||||
|
||||
void MidiDriver_TOWNS::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
|
||||
void IMuseDriver_FMTowns::setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) {
|
||||
_timerProc = timer_proc;
|
||||
_timerProcPara = timer_param;
|
||||
}
|
||||
|
||||
uint32 MidiDriver_TOWNS::getBaseTempo() {
|
||||
uint32 IMuseDriver_FMTowns::getBaseTempo() {
|
||||
return _baseTempo;
|
||||
}
|
||||
|
||||
MidiChannel *MidiDriver_TOWNS::allocateChannel() {
|
||||
MidiChannel *IMuseDriver_FMTowns::allocateChannel() {
|
||||
if (!_isOpen)
|
||||
return nullptr;
|
||||
|
||||
|
@ -976,11 +979,11 @@ MidiChannel *MidiDriver_TOWNS::allocateChannel() {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
MidiChannel *MidiDriver_TOWNS::getPercussionChannel() {
|
||||
MidiChannel *IMuseDriver_FMTowns::getPercussionChannel() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MidiDriver_TOWNS::timerCallback(int timerId) {
|
||||
void IMuseDriver_FMTowns::timerCallback(int timerId) {
|
||||
if (!_isOpen)
|
||||
return;
|
||||
|
||||
|
@ -994,12 +997,12 @@ void MidiDriver_TOWNS::timerCallback(int timerId) {
|
|||
}
|
||||
}
|
||||
|
||||
void MidiDriver_TOWNS::updateParser() {
|
||||
void IMuseDriver_FMTowns::updateParser() {
|
||||
if (_timerProc)
|
||||
_timerProc(_timerProcPara);
|
||||
}
|
||||
|
||||
void MidiDriver_TOWNS::updateOutputChannels() {
|
||||
void IMuseDriver_FMTowns::updateOutputChannels() {
|
||||
_tickCounter += _baseTempo;
|
||||
while (_tickCounter >= 16667) {
|
||||
_tickCounter -= 16667;
|
||||
|
@ -1010,7 +1013,7 @@ void MidiDriver_TOWNS::updateOutputChannels() {
|
|||
}
|
||||
}
|
||||
|
||||
TownsMidiOutputChannel *MidiDriver_TOWNS::allocateOutputChannel(uint8 pri) {
|
||||
TownsMidiOutputChannel *IMuseDriver_FMTowns::allocateOutputChannel(uint8 pri) {
|
||||
TownsMidiOutputChannel *res = nullptr;
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
|
@ -1033,7 +1036,9 @@ TownsMidiOutputChannel *MidiDriver_TOWNS::allocateOutputChannel(uint8 pri) {
|
|||
return res;
|
||||
}
|
||||
|
||||
int MidiDriver_TOWNS::randomValue(int para) {
|
||||
int IMuseDriver_FMTowns::randomValue(int para) {
|
||||
_rand = (_rand & 1) ? (_rand >> 1) ^ 0xb8 : (_rand >> 1);
|
||||
return (_rand * para) >> 8;
|
||||
}
|
||||
|
||||
} // end of namespace Scumm
|
||||
|
|
|
@ -25,17 +25,18 @@
|
|||
#include "audio/softsynth/fmtowns_pc98/towns_audio.h"
|
||||
#include "audio/mididrv.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class TownsMidiOutputChannel;
|
||||
class TownsMidiInputChannel;
|
||||
class TownsMidiChanState;
|
||||
|
||||
class MidiDriver_TOWNS : public MidiDriver, public TownsAudioInterfacePluginDriver {
|
||||
class IMuseDriver_FMTowns : public MidiDriver, public TownsAudioInterfacePluginDriver {
|
||||
friend class TownsMidiInputChannel;
|
||||
friend class TownsMidiOutputChannel;
|
||||
public:
|
||||
MidiDriver_TOWNS(Audio::Mixer *mixer);
|
||||
~MidiDriver_TOWNS() override;
|
||||
IMuseDriver_FMTowns(Audio::Mixer *mixer);
|
||||
~IMuseDriver_FMTowns() override;
|
||||
|
||||
int open() override;
|
||||
bool isOpen() const override { return _isOpen; }
|
||||
|
@ -79,4 +80,6 @@ private:
|
|||
const uint16 _baseTempo;
|
||||
};
|
||||
|
||||
} // end of namespace Scumm
|
||||
|
||||
#endif
|
||||
|
|
195
engines/scumm/imuse/drivers/gmidi.cpp
Normal file
195
engines/scumm/imuse/drivers/gmidi.cpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
/* 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 "common/debug.h"
|
||||
#include "common/system.h"
|
||||
#include "scumm/imuse/drivers/gmidi.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
IMuseDriver_GMidi::IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem) : MidiDriver(), _drv(nullptr), _gsMode(rolandGSMode), _newSystem(newSystem) {
|
||||
_drv = MidiDriver::createMidi(dev);
|
||||
assert(_drv);
|
||||
}
|
||||
|
||||
IMuseDriver_GMidi::~IMuseDriver_GMidi() {
|
||||
delete _drv;
|
||||
}
|
||||
|
||||
int IMuseDriver_GMidi::open() {
|
||||
if (!_drv)
|
||||
return MERR_CANNOT_CONNECT;
|
||||
|
||||
int res = _drv->open();
|
||||
|
||||
if (_gsMode)
|
||||
initDeviceAsRolandGS();
|
||||
else
|
||||
initDevice();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
MidiChannel *IMuseDriver_GMidi::allocateChannel() {
|
||||
if (!_newSystem)
|
||||
return _drv->allocateChannel();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MidiChannel *IMuseDriver_GMidi::getPercussionChannel() {
|
||||
if (!_newSystem)
|
||||
return _drv->getPercussionChannel();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void IMuseDriver_GMidi::initDevice() {
|
||||
// These are the init messages from the DOTT General Midi
|
||||
// driver. This is the major part of the bug fix for bug
|
||||
// no. 13460 ("DOTT: Incorrect MIDI pitch bending").
|
||||
// SAMNMAX has some less of the default settings (since
|
||||
// the driver works a bit different), but it uses the same
|
||||
// values for the pitch bend range.
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
send(0x0064B0 | i);
|
||||
send(0x0065B0 | i);
|
||||
send(0x1006B0 | i);
|
||||
send(0x7F07B0 | i);
|
||||
send(0x3F0AB0 | i);
|
||||
send(0x0000C0 | i);
|
||||
send(0x4000E0 | i);
|
||||
send(0x0001B0 | i);
|
||||
send(0x0040B0 | i);
|
||||
send(0x405BB0 | i);
|
||||
send(0x005DB0 | i);
|
||||
send(0x0000B0 | i);
|
||||
send(0x007BB0 | i);
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseDriver_GMidi::initDeviceAsRolandGS() {
|
||||
byte buffer[12];
|
||||
int i;
|
||||
|
||||
// General MIDI System On message
|
||||
// Resets all GM devices to default settings
|
||||
memcpy(&buffer[0], "\x7E\x7F\x09\x01", 4);
|
||||
_drv->sysEx(buffer, 4);
|
||||
debug(2, "GM SysEx: GM System On");
|
||||
g_system->delayMillis(200);
|
||||
|
||||
// All GS devices recognize the GS Reset command,
|
||||
// even using Roland's ID. It is impractical to
|
||||
// support other manufacturers' devices for
|
||||
// further GS settings, as there are limitless
|
||||
// numbers of them out there that would each
|
||||
// require individual SysEx commands with unique IDs.
|
||||
|
||||
// Roland GS SysEx ID
|
||||
memcpy(&buffer[0], "\x41\x10\x42\x12", 4);
|
||||
|
||||
// GS Reset
|
||||
memcpy(&buffer[4], "\x40\x00\x7F\x00\x41", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: GS Reset");
|
||||
g_system->delayMillis(200);
|
||||
|
||||
// Set global Master Tune to 442.0kHz, as on the MT-32
|
||||
memcpy(&buffer[4], "\x40\x00\x00\x00\x04\x04\x0F\x29", 8);
|
||||
_drv->sysEx(buffer, 12);
|
||||
debug(2, "GS SysEx: Master Tune set to 442.0kHz");
|
||||
|
||||
// Note: All Roland GS devices support CM-64/32L maps
|
||||
|
||||
// Set Channels 1-16 to SC-55 Map, then CM-64/32L Variation
|
||||
for (i = 0; i < 16; ++i) {
|
||||
_drv->send((127 << 16) | (0 << 8) | (0xB0 | i));
|
||||
_drv->send((1 << 16) | (32 << 8) | (0xB0 | i));
|
||||
_drv->send((0 << 16) | (0 << 8) | (0xC0 | i));
|
||||
}
|
||||
debug(2, "GS Program Change: CM-64/32L Map Selected");
|
||||
|
||||
// Set Percussion Channel to SC-55 Map (CC#32, 01H), then
|
||||
// Switch Drum Map to CM-64/32L (MT-32 Compatible Drums)
|
||||
getPercussionChannel()->controlChange(0, 0);
|
||||
getPercussionChannel()->controlChange(32, 1);
|
||||
_drv->send(127 << 8 | 0xC0 | 9);
|
||||
debug(2, "GS Program Change: Drum Map is CM-64/32L");
|
||||
|
||||
// Set Master Chorus to 0. The MT-32 has no chorus capability.
|
||||
memcpy(&buffer[4], "\x40\x01\x3A\x00\x05", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Master Chorus Level is 0");
|
||||
|
||||
// Set Channels 1-16 Reverb to 64, which is the
|
||||
// equivalent of MT-32 default Reverb Level 5
|
||||
for (i = 0; i < 16; ++i)
|
||||
_drv->send((64 << 16) | (91 << 8) | (0xB0 | i));
|
||||
debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64");
|
||||
|
||||
// Set Channels 1-16 Pitch Bend Sensitivity to
|
||||
// 12 semitones; then lock the RPN by setting null.
|
||||
for (i = 0; i < 16; ++i)
|
||||
_drv->setPitchBendRange(i, 12);
|
||||
debug(2, "GM Controller 6 Change: Channels 1-16 Pitch Bend Sensitivity is 12 semitones");
|
||||
|
||||
// Set channels 1-16 Mod. LFO1 Pitch Depth to 4
|
||||
memcpy(&buffer[4], "\x40\x20\x04\x04\x18", 5);
|
||||
for (i = 0; i < 16; ++i) {
|
||||
buffer[5] = 0x20 + i;
|
||||
buffer[8] = 0x18 - i;
|
||||
_drv->sysEx(buffer, 9);
|
||||
}
|
||||
|
||||
debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4");
|
||||
|
||||
// Set Percussion Channel Expression to 80
|
||||
getPercussionChannel()->controlChange(11, 80);
|
||||
debug(2, "GM Controller 11 Change: Percussion Channel Expression Level is 80");
|
||||
|
||||
// Turn off Percussion Channel Rx. Expression so that
|
||||
// Expression cannot be modified. I don't know why, but
|
||||
// Roland does it this way.
|
||||
memcpy(&buffer[4], "\x40\x10\x0E\x00\x22", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Percussion Channel Rx. Expression is OFF");
|
||||
|
||||
// Change Reverb Character to 0. I don't think this
|
||||
// sounds most like MT-32, but apparently Roland does.
|
||||
memcpy(&buffer[4], "\x40\x01\x31\x00\x0E", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Reverb Character is 0");
|
||||
|
||||
// Change Reverb Pre-LF to 4, which is similar to
|
||||
// what MT-32 reverb does.
|
||||
memcpy(&buffer[4], "\x40\x01\x32\x04\x09", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Reverb Pre-LF is 4");
|
||||
|
||||
// Change Reverb Time to 106; the decay on Hall 2
|
||||
// Reverb is too fast compared to the MT-32's
|
||||
memcpy(&buffer[4], "\x40\x01\x34\x6A\x21", 5);
|
||||
_drv->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Reverb Time is 106");
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
60
engines/scumm/imuse/drivers/gmidi.h
Normal file
60
engines/scumm/imuse/drivers/gmidi.h
Normal file
|
@ -0,0 +1,60 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SCUMM_IMUSE_DRV_GMIDI_H
|
||||
#define SCUMM_IMUSE_DRV_GMIDI_H
|
||||
|
||||
#include "audio/mididrv.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class IMuseDriver_GMidi : public MidiDriver {
|
||||
public:
|
||||
IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem);
|
||||
~IMuseDriver_GMidi() override;
|
||||
|
||||
int open() override;
|
||||
|
||||
// Just pass these through...
|
||||
bool isOpen() const override { return _drv ? _drv->isOpen() : false; }
|
||||
void close() override { if (_drv) _drv->close(); }
|
||||
uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; }
|
||||
void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
|
||||
uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
|
||||
void send(uint32 b) override { if (_drv) _drv->send(b); };
|
||||
void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); }
|
||||
|
||||
// Channel allocation functions
|
||||
MidiChannel *allocateChannel() override;
|
||||
MidiChannel *getPercussionChannel() override;
|
||||
|
||||
private:
|
||||
void initDevice();
|
||||
void initDeviceAsRolandGS();
|
||||
|
||||
MidiDriver *_drv;
|
||||
const bool _gsMode;
|
||||
const bool _newSystem;
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
||||
#endif
|
|
@ -27,14 +27,19 @@
|
|||
|
||||
namespace Scumm {
|
||||
|
||||
MacM68kDriver::MacM68kDriver(Audio::Mixer *mixer)
|
||||
: MidiDriver_Emulated(mixer) {
|
||||
IMuseDriver_MacM68k::IMuseDriver_MacM68k(Audio::Mixer *mixer)
|
||||
: MidiDriver_Emulated(mixer), _mixBuffer(nullptr), _mixBufferLength(0), _volumeTable(nullptr), _lastUsedVoiceChannel(0) {
|
||||
memset(_channels, 0, sizeof(_channels));
|
||||
memset(_pitchTable, 0, sizeof(_pitchTable));
|
||||
memset(_voiceChannels, 0, sizeof(_voiceChannels));
|
||||
}
|
||||
|
||||
MacM68kDriver::~MacM68kDriver() {
|
||||
IMuseDriver_MacM68k::~IMuseDriver_MacM68k() {
|
||||
for (uint i = 0; i < ARRAYSIZE(_channels); ++i)
|
||||
delete _channels[i];
|
||||
}
|
||||
|
||||
int MacM68kDriver::open() {
|
||||
int IMuseDriver_MacM68k::open() {
|
||||
if (_isOpen) {
|
||||
return MERR_ALREADY_OPEN;
|
||||
}
|
||||
|
@ -45,7 +50,8 @@ int MacM68kDriver::open() {
|
|||
}
|
||||
|
||||
for (uint i = 0; i < ARRAYSIZE(_channels); ++i) {
|
||||
_channels[i].init(this, i);
|
||||
delete _channels[i];
|
||||
_channels[i] = new MidiChannel_MacM68k(this, i);
|
||||
}
|
||||
|
||||
memset(_voiceChannels, 0, sizeof(_voiceChannels));
|
||||
|
@ -88,7 +94,7 @@ int MacM68kDriver::open() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void MacM68kDriver::close() {
|
||||
void IMuseDriver_MacM68k::close() {
|
||||
if (!_isOpen) {
|
||||
return;
|
||||
}
|
||||
|
@ -106,25 +112,21 @@ void MacM68kDriver::close() {
|
|||
_mixBufferLength = 0;
|
||||
}
|
||||
|
||||
void MacM68kDriver::send(uint32 d) {
|
||||
void IMuseDriver_MacM68k::send(uint32 d) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void MacM68kDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
MidiChannel *MacM68kDriver::allocateChannel() {
|
||||
MidiChannel *IMuseDriver_MacM68k::allocateChannel() {
|
||||
for (uint i = 0; i < ARRAYSIZE(_channels); ++i) {
|
||||
if (_channels[i].allocate()) {
|
||||
return &_channels[i];
|
||||
if (_channels[i]->allocate()) {
|
||||
return _channels[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const {
|
||||
IMuseDriver_MacM68k::Instrument IMuseDriver_MacM68k::getInstrument(int idx) const {
|
||||
InstrumentMap::const_iterator i = _instruments.find(idx);
|
||||
if (i != _instruments.end()) {
|
||||
return i->_value;
|
||||
|
@ -133,7 +135,7 @@ MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const {
|
|||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::generateSamples(int16 *buf, int len) {
|
||||
void IMuseDriver_MacM68k::generateSamples(int16 *buf, int len) {
|
||||
int silentChannels = 0;
|
||||
|
||||
if (_mixBufferLength < len) {
|
||||
|
@ -191,7 +193,7 @@ void MacM68kDriver::generateSamples(int16 *buf, int len) {
|
|||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::loadAllInstruments() {
|
||||
void IMuseDriver_MacM68k::loadAllInstruments() {
|
||||
Common::MacResManager resource;
|
||||
if (resource.open("iMUSE Setups")) {
|
||||
if (!resource.hasResFork()) {
|
||||
|
@ -225,7 +227,7 @@ void MacM68kDriver::loadAllInstruments() {
|
|||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) {
|
||||
void IMuseDriver_MacM68k::addInstrument(int idx, Common::SeekableReadStream *data) {
|
||||
// We parse the "SND" files manually here, since we need special data
|
||||
// from their header and need to work on them raw while mixing.
|
||||
data->skip(2);
|
||||
|
@ -251,7 +253,7 @@ void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) {
|
|||
_instruments[idx] = inst;
|
||||
}
|
||||
|
||||
void MacM68kDriver::setPitch(OutputChannel *out, int frequency) {
|
||||
void IMuseDriver_MacM68k::setPitch(OutputChannel *out, int frequency) {
|
||||
out->frequency = frequency;
|
||||
out->isFinished = false;
|
||||
|
||||
|
@ -266,7 +268,7 @@ void MacM68kDriver::setPitch(OutputChannel *out, int frequency) {
|
|||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::VoiceChannel::off() {
|
||||
void IMuseDriver_MacM68k::VoiceChannel::off() {
|
||||
if (out.start) {
|
||||
out.isFinished = true;
|
||||
}
|
||||
|
@ -275,14 +277,14 @@ void MacM68kDriver::VoiceChannel::off() {
|
|||
part = nullptr;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::release() {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::release() {
|
||||
_allocated = false;
|
||||
while (_voice) {
|
||||
_voice->off();
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::send(uint32 b) {
|
||||
uint8 type = b & 0xF0;
|
||||
uint8 p1 = (b >> 8) & 0xFF;
|
||||
uint8 p2 = (b >> 16) & 0xFF;
|
||||
|
@ -313,7 +315,7 @@ void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) {
|
|||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::noteOff(byte note) {
|
||||
for (VoiceChannel *i = _voice; i; i = i->next) {
|
||||
if (i->note == note) {
|
||||
if (_sustain) {
|
||||
|
@ -325,7 +327,7 @@ void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) {
|
|||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::noteOn(byte note, byte velocity) {
|
||||
// Do not start a not unless there is an instrument set up
|
||||
if (!_instrument.data) {
|
||||
return;
|
||||
|
@ -366,18 +368,18 @@ void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) {
|
|||
voice->out.subPos = 0;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::programChange(byte program) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::programChange(byte program) {
|
||||
_instrument = _owner->getInstrument(program + kProgramChangeBase);
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::pitchBend(int16 bend) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::pitchBend(int16 bend) {
|
||||
_pitchBend = (bend * _pitchBendFactor) >> 6;
|
||||
for (VoiceChannel *i = _voice; i; i = i->next) {
|
||||
_owner->setPitch(&i->out, (i->note << 7) + _pitchBend);
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::controlChange(byte control, byte value) {
|
||||
switch (control) {
|
||||
// volume change
|
||||
case 7:
|
||||
|
@ -412,28 +414,22 @@ void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value)
|
|||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::pitchBendFactor(byte value) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::pitchBendFactor(byte value) {
|
||||
_pitchBendFactor = value;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::priority(byte value) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::priority(byte value) {
|
||||
_priority = value;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) {
|
||||
assert(instr);
|
||||
if (type == 'MAC ') {
|
||||
_instrument = _owner->getInstrument(*instr + kSysExBase);
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::init(MacM68kDriver *owner, byte channel) {
|
||||
_owner = owner;
|
||||
_number = channel;
|
||||
_allocated = false;
|
||||
}
|
||||
|
||||
bool MacM68kDriver::MidiChannel_MacM68k::allocate() {
|
||||
bool IMuseDriver_MacM68k::MidiChannel_MacM68k::allocate() {
|
||||
if (_allocated) {
|
||||
return false;
|
||||
}
|
||||
|
@ -448,7 +444,7 @@ bool MacM68kDriver::MidiChannel_MacM68k::allocate() {
|
|||
return true;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) {
|
||||
voice->next = _voice;
|
||||
voice->prev = nullptr;
|
||||
voice->part = this;
|
||||
|
@ -458,7 +454,7 @@ void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) {
|
|||
_voice = voice;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) {
|
||||
void IMuseDriver_MacM68k::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) {
|
||||
VoiceChannel *i = _voice;
|
||||
while (i && i != voice) {
|
||||
i = i->next;
|
||||
|
@ -477,7 +473,7 @@ void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) {
|
|||
}
|
||||
}
|
||||
|
||||
MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) {
|
||||
IMuseDriver_MacM68k::VoiceChannel *IMuseDriver_MacM68k::allocateVoice(int priority) {
|
||||
VoiceChannel *channel = nullptr;
|
||||
for (int i = 0; i < kChannelCount; ++i) {
|
||||
if (++_lastUsedVoiceChannel == kChannelCount) {
|
||||
|
@ -504,7 +500,7 @@ MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) {
|
|||
return channel;
|
||||
}
|
||||
|
||||
const int MacM68kDriver::_volumeBaseTable[32] = {
|
||||
const int IMuseDriver_MacM68k::_volumeBaseTable[32] = {
|
||||
0, 0, 1, 1, 2, 3, 5, 6,
|
||||
8, 11, 13, 16, 19, 22, 26, 30,
|
||||
34, 38, 43, 48, 53, 58, 64, 70,
|
||||
|
|
|
@ -32,20 +32,19 @@ class SeekableReadStream;
|
|||
|
||||
namespace Scumm {
|
||||
|
||||
class MacM68kDriver : public MidiDriver_Emulated {
|
||||
class IMuseDriver_MacM68k : public MidiDriver_Emulated {
|
||||
friend class MidiChannel_MacM68k;
|
||||
public:
|
||||
MacM68kDriver(Audio::Mixer *mixer);
|
||||
~MacM68kDriver() override;
|
||||
IMuseDriver_MacM68k(Audio::Mixer *mixer);
|
||||
~IMuseDriver_MacM68k() override;
|
||||
|
||||
int open() override;
|
||||
void close() override;
|
||||
|
||||
void send(uint32 d) override;
|
||||
void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) override;
|
||||
|
||||
MidiChannel *allocateChannel() override;
|
||||
MidiChannel *getPercussionChannel() override { return 0; }
|
||||
MidiChannel *getPercussionChannel() override { return nullptr; }
|
||||
|
||||
bool isStereo() const override { return false; }
|
||||
int getRate() const override {
|
||||
|
@ -127,8 +126,9 @@ private:
|
|||
};
|
||||
|
||||
class MidiChannel_MacM68k : public MidiChannel {
|
||||
friend class MacM68kDriver;
|
||||
friend class IMuseDriver_MacM68k;
|
||||
public:
|
||||
MidiChannel_MacM68k(IMuseDriver_MacM68k *driver, byte number) : MidiChannel(), _owner(driver), _number(number), _allocated(false) {}
|
||||
MidiDriver *device() override { return _owner; }
|
||||
byte getNumber() override { return _number; }
|
||||
void release() override;
|
||||
|
@ -143,15 +143,14 @@ private:
|
|||
void priority(byte value) override;
|
||||
void sysEx_customInstrument(uint32 type, const byte *instr) override;
|
||||
|
||||
void init(MacM68kDriver *owner, byte channel);
|
||||
bool allocate();
|
||||
|
||||
void addVoice(VoiceChannel *voice);
|
||||
void removeVoice(VoiceChannel *voice);
|
||||
private:
|
||||
MacM68kDriver *_owner;
|
||||
IMuseDriver_MacM68k *_owner;
|
||||
const byte _number;
|
||||
bool _allocated;
|
||||
int _number;
|
||||
|
||||
VoiceChannel *_voice;
|
||||
int _priority;
|
||||
|
@ -162,7 +161,7 @@ private:
|
|||
int _volume;
|
||||
};
|
||||
|
||||
MidiChannel_MacM68k _channels[32];
|
||||
MidiChannel_MacM68k *_channels[32];
|
||||
|
||||
enum {
|
||||
kChannelCount = 8
|
||||
|
|
103
engines/scumm/imuse/drivers/mt32.cpp
Normal file
103
engines/scumm/imuse/drivers/mt32.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
/* 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 "common/str.h"
|
||||
#include "common/system.h"
|
||||
#include "base/version.h"
|
||||
#include "scumm/imuse/drivers/mt32.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : MidiDriver(), _newSystem(newSystem), _drv(nullptr) {
|
||||
_drv = MidiDriver::createMidi(dev);
|
||||
assert(_drv);
|
||||
|
||||
if (!_newSystem)
|
||||
_drv->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
||||
}
|
||||
|
||||
IMuseDriver_MT32::~IMuseDriver_MT32() {
|
||||
delete _drv;
|
||||
}
|
||||
|
||||
int IMuseDriver_MT32::open() {
|
||||
if (!_drv)
|
||||
return MERR_CANNOT_CONNECT;
|
||||
|
||||
int res = _drv->open();
|
||||
|
||||
initDevice();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
MidiChannel *IMuseDriver_MT32::allocateChannel() {
|
||||
if (!_newSystem)
|
||||
return _drv->allocateChannel();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MidiChannel *IMuseDriver_MT32::getPercussionChannel() {
|
||||
if (!_newSystem)
|
||||
return _drv->getPercussionChannel();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void IMuseDriver_MT32::initDevice() {
|
||||
byte buffer[52];
|
||||
|
||||
// Reset the MT-32
|
||||
_drv->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
|
||||
g_system->delayMillis(250);
|
||||
|
||||
// Setup master tune, reverb mode, reverb time, reverb level,
|
||||
// channel mapping, partial reserve and master volume
|
||||
_drv->sysEx((const byte *) "\x41\x10\x16\x12\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 31);
|
||||
g_system->delayMillis(250);
|
||||
|
||||
// Map percussion to notes 24 - 34 without reverb
|
||||
if (!_newSystem) {
|
||||
_drv->sysEx((const byte *) "\x41\x10\x16\x12\x03\x01\x10\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00\x44", 52);
|
||||
g_system->delayMillis(250);
|
||||
}
|
||||
|
||||
// Compute version string (truncated to 20 chars max.)
|
||||
Common::String infoStr = "ScummVM ";
|
||||
infoStr += gScummVMVersion;
|
||||
int len = infoStr.size();
|
||||
if (len > 20)
|
||||
len = 20;
|
||||
|
||||
// Display a welcome message on MT-32 displays.
|
||||
memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7);
|
||||
memcpy(&buffer[7], " ", 20);
|
||||
memcpy(buffer + 7 + (20 - len) / 2, infoStr.c_str(), len);
|
||||
byte checksum = 0;
|
||||
for (int i = 4; i < 27; ++i)
|
||||
checksum -= buffer[i];
|
||||
buffer[27] = checksum & 0x7F;
|
||||
_drv->sysEx(buffer, 28);
|
||||
g_system->delayMillis(1000);
|
||||
}
|
||||
|
||||
} // End of namespace Scumm
|
59
engines/scumm/imuse/drivers/mt32.h
Normal file
59
engines/scumm/imuse/drivers/mt32.h
Normal file
|
@ -0,0 +1,59 @@
|
|||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SCUMM_IMUSE_DRV_MT32_H
|
||||
#define SCUMM_IMUSE_DRV_MT32_H
|
||||
|
||||
#include "audio/mididrv.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class IMuseDriver_MT32 : public MidiDriver {
|
||||
public:
|
||||
IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem);
|
||||
~IMuseDriver_MT32() override;
|
||||
|
||||
int open() override;
|
||||
|
||||
// Just pass these through...
|
||||
bool isOpen() const override { return _drv ? _drv->isOpen() : false; }
|
||||
void close() override { if (_drv) _drv->close(); }
|
||||
uint32 property(int prop, uint32 param) override { return _drv ? _drv->property(prop, param) : 0; }
|
||||
void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
|
||||
uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
|
||||
void send(uint32 b) override { if (_drv) _drv->send(b); };
|
||||
void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); }
|
||||
void setPitchBendRange(byte channel, uint range) override { if (_drv) _drv->setPitchBendRange(channel, range); }
|
||||
|
||||
// Channel allocation functions
|
||||
MidiChannel *allocateChannel() override;
|
||||
MidiChannel *getPercussionChannel() override;
|
||||
|
||||
private:
|
||||
void initDevice();
|
||||
|
||||
MidiDriver *_drv;
|
||||
const bool _newSystem;
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
||||
#endif
|
|
@ -25,22 +25,26 @@
|
|||
|
||||
namespace Scumm {
|
||||
|
||||
PcSpkDriver::PcSpkDriver(Audio::Mixer *mixer)
|
||||
: MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) {
|
||||
IMuseDriver_PCSpk::IMuseDriver_PCSpk(Audio::Mixer *mixer)
|
||||
: MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) , _activeChannel(nullptr), _lastActiveChannel(nullptr), _lastActiveOut(0), _effectTimer(0), _randBase(1) {
|
||||
memset(_channels, 0, sizeof(_channels));
|
||||
}
|
||||
|
||||
PcSpkDriver::~PcSpkDriver() {
|
||||
IMuseDriver_PCSpk::~IMuseDriver_PCSpk() {
|
||||
close();
|
||||
}
|
||||
|
||||
int PcSpkDriver::open() {
|
||||
int IMuseDriver_PCSpk::open() {
|
||||
if (_isOpen)
|
||||
return MERR_ALREADY_OPEN;
|
||||
|
||||
MidiDriver_Emulated::open();
|
||||
|
||||
for (uint i = 0; i < 6; ++i)
|
||||
_channels[i].init(this, i);
|
||||
for (uint i = 0; i < 6; ++i) {
|
||||
delete _channels[i];
|
||||
_channels[i] = new MidiChannel_PcSpk(this, i);
|
||||
}
|
||||
|
||||
_activeChannel = nullptr;
|
||||
_effectTimer = 0;
|
||||
_randBase = 1;
|
||||
|
@ -59,44 +63,41 @@ int PcSpkDriver::open() {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void PcSpkDriver::close() {
|
||||
void IMuseDriver_PCSpk::close() {
|
||||
if (!_isOpen)
|
||||
return;
|
||||
_isOpen = false;
|
||||
|
||||
_mixer->stopHandle(_mixerSoundHandle);
|
||||
|
||||
for (uint i = 0; i < 6; ++i)
|
||||
delete _channels[i];
|
||||
}
|
||||
|
||||
void PcSpkDriver::send(uint32 d) {
|
||||
void IMuseDriver_PCSpk::send(uint32 d) {
|
||||
assert((d & 0x0F) < 6);
|
||||
_channels[(d & 0x0F)].send(d);
|
||||
_channels[(d & 0x0F)]->send(d);
|
||||
}
|
||||
|
||||
void PcSpkDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
|
||||
assert(channel < 6);
|
||||
if (type == 'SPK ')
|
||||
_channels[channel].sysEx_customInstrument(type, instr);
|
||||
}
|
||||
|
||||
MidiChannel *PcSpkDriver::allocateChannel() {
|
||||
MidiChannel *IMuseDriver_PCSpk::allocateChannel() {
|
||||
for (uint i = 0; i < 6; ++i) {
|
||||
if (_channels[i].allocate())
|
||||
return &_channels[i];
|
||||
if (_channels[i]->allocate())
|
||||
return _channels[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void PcSpkDriver::generateSamples(int16 *buf, int len) {
|
||||
void IMuseDriver_PCSpk::generateSamples(int16 *buf, int len) {
|
||||
_pcSpk.readBuffer(buf, len);
|
||||
}
|
||||
|
||||
void PcSpkDriver::onTimer() {
|
||||
void IMuseDriver_PCSpk::onTimer() {
|
||||
if (!_activeChannel)
|
||||
return;
|
||||
|
||||
for (uint i = 0; i < 6; ++i) {
|
||||
OutputChannel &out = _channels[i]._out;
|
||||
OutputChannel &out = _channels[i]->_out;
|
||||
|
||||
if (!out.active)
|
||||
continue;
|
||||
|
@ -113,9 +114,9 @@ void PcSpkDriver::onTimer() {
|
|||
_effectTimer = 0;
|
||||
|
||||
if (out.effectEnvelopeA.state)
|
||||
updateEffectGenerator(_channels[i], out.effectEnvelopeA, out.effectDefA);
|
||||
updateEffectGenerator(*_channels[i], out.effectEnvelopeA, out.effectDefA);
|
||||
if (out.effectEnvelopeB.state)
|
||||
updateEffectGenerator(_channels[i], out.effectEnvelopeB, out.effectDefB);
|
||||
updateEffectGenerator(*_channels[i], out.effectEnvelopeB, out.effectDefB);
|
||||
}
|
||||
} else {
|
||||
out.active = 0;
|
||||
|
@ -133,13 +134,13 @@ void PcSpkDriver::onTimer() {
|
|||
}
|
||||
}
|
||||
|
||||
void PcSpkDriver::updateNote() {
|
||||
void IMuseDriver_PCSpk::updateNote() {
|
||||
uint8 priority = 0;
|
||||
_activeChannel = nullptr;
|
||||
for (uint i = 0; i < 6; ++i) {
|
||||
if (_channels[i]._allocated && _channels[i]._out.active && _channels[i]._priority >= priority) {
|
||||
priority = _channels[i]._priority;
|
||||
_activeChannel = &_channels[i];
|
||||
if (_channels[i]->_allocated && _channels[i]->_out.active && _channels[i]->_priority >= priority) {
|
||||
priority = _channels[i]->_priority;
|
||||
_activeChannel = _channels[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,7 +153,7 @@ void PcSpkDriver::updateNote() {
|
|||
}
|
||||
}
|
||||
|
||||
void PcSpkDriver::output(uint16 out) {
|
||||
void IMuseDriver_PCSpk::output(uint16 out) {
|
||||
byte v1 = (out >> 7) & 0xFF;
|
||||
byte v2 = (out >> 2) & 0x1E;
|
||||
|
||||
|
@ -170,14 +171,11 @@ void PcSpkDriver::output(uint16 out) {
|
|||
}
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::init(PcSpkDriver *owner, byte channel) {
|
||||
_owner = owner;
|
||||
_channel = channel;
|
||||
_allocated = false;
|
||||
IMuseDriver_PCSpk::MidiChannel_PcSpk::MidiChannel_PcSpk(IMuseDriver_PCSpk *owner, byte number) : MidiChannel(), _owner(owner), _number(number), _allocated(false) {
|
||||
memset(&_out, 0, sizeof(_out));
|
||||
}
|
||||
|
||||
bool PcSpkDriver::MidiChannel_PcSpk::allocate() {
|
||||
bool IMuseDriver_PCSpk::MidiChannel_PcSpk::allocate() {
|
||||
if (_allocated)
|
||||
return false;
|
||||
|
||||
|
@ -190,21 +188,13 @@ bool PcSpkDriver::MidiChannel_PcSpk::allocate() {
|
|||
return true;
|
||||
}
|
||||
|
||||
MidiDriver *PcSpkDriver::MidiChannel_PcSpk::device() {
|
||||
return _owner;
|
||||
}
|
||||
|
||||
byte PcSpkDriver::MidiChannel_PcSpk::getNumber() {
|
||||
return _channel;
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::release() {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::release() {
|
||||
_out.active = 0;
|
||||
_allocated = false;
|
||||
_owner->updateNote();
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::send(uint32 b) {
|
||||
uint8 type = b & 0xF0;
|
||||
uint8 p1 = (b >> 8) & 0xFF;
|
||||
uint8 p2 = (b >> 16) & 0xFF;
|
||||
|
@ -234,7 +224,7 @@ void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) {
|
|||
}
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::noteOff(byte note) {
|
||||
if (!_allocated)
|
||||
return;
|
||||
|
||||
|
@ -249,7 +239,7 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) {
|
|||
}
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
|
||||
if (!_allocated)
|
||||
return;
|
||||
|
||||
|
@ -257,7 +247,7 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
|
|||
_out.sustainNoteOff = 0;
|
||||
_out.length = _instrument[0];
|
||||
|
||||
if (_instrument[4] * 256 < ARRAYSIZE(PcSpkDriver::_outInstrumentData))
|
||||
if (_instrument[4] * 256 < ARRAYSIZE(IMuseDriver_PCSpk::_outInstrumentData))
|
||||
_out.instrument = _owner->_outInstrumentData + _instrument[4] * 256;
|
||||
else
|
||||
_out.instrument = nullptr;
|
||||
|
@ -278,7 +268,7 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
|
|||
}
|
||||
_owner->updateNote();
|
||||
|
||||
_out.unkC += PcSpkDriver::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4));
|
||||
_out.unkC += IMuseDriver_PCSpk::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4));
|
||||
if (_out.unkC > 63)
|
||||
_out.unkC = 63;
|
||||
|
||||
|
@ -289,16 +279,16 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
|
|||
_owner->setupEffects(*this, _out.effectEnvelopeB, _out.effectDefB, _instrument[14], _instrument + 15);
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::programChange(byte program) {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::programChange(byte program) {
|
||||
// Nothing to implement here, the iMuse code takes care of passing us the
|
||||
// instrument data.
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::pitchBend(int16 bend) {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::pitchBend(int16 bend) {
|
||||
_pitchBend = (bend * _pitchBendFactor) >> 6;
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::controlChange(byte control, byte value) {
|
||||
switch (control) {
|
||||
case 1:
|
||||
if (_out.effectEnvelopeA.state && _out.effectDefA.useModWheel)
|
||||
|
@ -338,19 +328,19 @@ void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) {
|
|||
}
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::pitchBendFactor(byte value) {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::pitchBendFactor(byte value) {
|
||||
_pitchBendFactor = value;
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::priority(byte value) {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::priority(byte value) {
|
||||
_priority = value;
|
||||
}
|
||||
|
||||
void PcSpkDriver::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) {
|
||||
void IMuseDriver_PCSpk::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) {
|
||||
memcpy(_instrument, instr, sizeof(_instrument));
|
||||
}
|
||||
|
||||
uint8 PcSpkDriver::getEffectModifier(uint16 level) {
|
||||
uint8 IMuseDriver_PCSpk::getEffectModifier(uint16 level) {
|
||||
uint8 base = level / 32;
|
||||
uint8 index = level % 32;
|
||||
|
||||
|
@ -360,7 +350,7 @@ uint8 PcSpkDriver::getEffectModifier(uint16 level) {
|
|||
return (base * (index + 1)) >> 5;
|
||||
}
|
||||
|
||||
int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) {
|
||||
int16 IMuseDriver_PCSpk::getEffectModLevel(int16 level, int8 mod) {
|
||||
if (!mod) {
|
||||
return 0;
|
||||
} else if (mod == 31) {
|
||||
|
@ -380,7 +370,7 @@ int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) {
|
|||
}
|
||||
}
|
||||
|
||||
int16 PcSpkDriver::getRandScale(int16 input) {
|
||||
int16 IMuseDriver_PCSpk::getRandScale(int16 input) {
|
||||
if (_randBase & 1)
|
||||
_randBase = (_randBase >> 1) ^ 0xB8;
|
||||
else
|
||||
|
@ -389,7 +379,7 @@ int16 PcSpkDriver::getRandScale(int16 input) {
|
|||
return (_randBase * input) >> 8;
|
||||
}
|
||||
|
||||
void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) {
|
||||
void IMuseDriver_PCSpk::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) {
|
||||
def.phase = 0;
|
||||
def.useModWheel = flags & 0x40;
|
||||
env.loop = flags & 0x20;
|
||||
|
@ -446,7 +436,7 @@ void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, Eff
|
|||
startEffect(env, data);
|
||||
}
|
||||
|
||||
void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) {
|
||||
void IMuseDriver_PCSpk::startEffect(EffectEnvelope &env, const byte *data) {
|
||||
env.state = 1;
|
||||
env.currentLevel = 0;
|
||||
env.modWheelLast = 31;
|
||||
|
@ -465,7 +455,7 @@ void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) {
|
|||
initNextEnvelopeState(env);
|
||||
}
|
||||
|
||||
void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) {
|
||||
void IMuseDriver_PCSpk::initNextEnvelopeState(EffectEnvelope &env) {
|
||||
uint8 lastState = env.state - 1;
|
||||
|
||||
uint16 stepCount = _effectEnvStepTable[getEffectModifier(((env.stateTargetLevels[lastState] & 0x7F) << 5) + env.modWheelSensitivity)];
|
||||
|
@ -501,7 +491,7 @@ void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) {
|
|||
env.changeCountRem = 0;
|
||||
}
|
||||
|
||||
void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) {
|
||||
void IMuseDriver_PCSpk::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) {
|
||||
if (advanceEffectEnvelope(env, def) & 1) {
|
||||
switch (def.type) {
|
||||
case 0: case 1:
|
||||
|
@ -537,7 +527,7 @@ void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope
|
|||
}
|
||||
}
|
||||
|
||||
uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) {
|
||||
uint8 IMuseDriver_PCSpk::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) {
|
||||
if (env.duration != 0) {
|
||||
env.duration -= 17;
|
||||
if (env.duration <= 0) {
|
||||
|
@ -584,7 +574,7 @@ uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &
|
|||
return changedFlags;
|
||||
}
|
||||
|
||||
const byte PcSpkDriver::_outInstrumentData[1024] = {
|
||||
const byte IMuseDriver_PCSpk::_outInstrumentData[1024] = {
|
||||
0x00, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x12, 0x15,
|
||||
0x18, 0x1B, 0x1E, 0x21, 0x24, 0x27, 0x2A, 0x2D,
|
||||
0x30, 0x33, 0x36, 0x39, 0x3B, 0x3E, 0x41, 0x43,
|
||||
|
@ -715,7 +705,7 @@ const byte PcSpkDriver::_outInstrumentData[1024] = {
|
|||
0xD4, 0xF7, 0x4A, 0x4A, 0xD0, 0x57, 0x68, 0x76
|
||||
};
|
||||
|
||||
const byte PcSpkDriver::_outputTable1[] = {
|
||||
const byte IMuseDriver_PCSpk::_outputTable1[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
@ -734,7 +724,7 @@ const byte PcSpkDriver::_outputTable1[] = {
|
|||
7, 7, 7, 7, 7, 7, 7, 7
|
||||
};
|
||||
|
||||
const byte PcSpkDriver::_outputTable2[] = {
|
||||
const byte IMuseDriver_PCSpk::_outputTable2[] = {
|
||||
0, 1, 2, 3,
|
||||
4, 5, 6, 7,
|
||||
8, 9, 10, 11,
|
||||
|
@ -769,7 +759,7 @@ const byte PcSpkDriver::_outputTable2[] = {
|
|||
4, 5, 6, 7
|
||||
};
|
||||
|
||||
const uint16 PcSpkDriver::_effectEnvStepTable[] = {
|
||||
const uint16 IMuseDriver_PCSpk::_effectEnvStepTable[] = {
|
||||
1, 2, 4, 5,
|
||||
6, 7, 8, 9,
|
||||
10, 12, 14, 16,
|
||||
|
@ -780,7 +770,7 @@ const uint16 PcSpkDriver::_effectEnvStepTable[] = {
|
|||
600, 860, 1200, 1600
|
||||
};
|
||||
|
||||
const uint16 PcSpkDriver::_frequencyTable[] = {
|
||||
const uint16 IMuseDriver_PCSpk::_frequencyTable[] = {
|
||||
0x8E84, 0x8E00, 0x8D7D, 0x8CFA,
|
||||
0x8C78, 0x8BF7, 0x8B76, 0x8AF5,
|
||||
0x8A75, 0x89F5, 0x8976, 0x88F7,
|
||||
|
|
|
@ -27,19 +27,18 @@
|
|||
|
||||
namespace Scumm {
|
||||
|
||||
class PcSpkDriver : public MidiDriver_Emulated {
|
||||
class IMuseDriver_PCSpk : public MidiDriver_Emulated {
|
||||
public:
|
||||
PcSpkDriver(Audio::Mixer *mixer);
|
||||
~PcSpkDriver() override;
|
||||
IMuseDriver_PCSpk(Audio::Mixer *mixer);
|
||||
~IMuseDriver_PCSpk() override;
|
||||
|
||||
int open() override;
|
||||
void close() override;
|
||||
|
||||
void send(uint32 d) override;
|
||||
void sysEx_customInstrument(byte channel, uint32 type, const byte *instr) override;
|
||||
|
||||
MidiChannel *allocateChannel() override;
|
||||
MidiChannel *getPercussionChannel() override { return 0; }
|
||||
MidiChannel *getPercussionChannel() override { return nullptr; }
|
||||
|
||||
bool isStereo() const override { return _pcSpk.isStereo(); }
|
||||
int getRate() const override { return _pcSpk.getRate(); }
|
||||
|
@ -103,9 +102,11 @@ private:
|
|||
int16 unk60;
|
||||
};
|
||||
|
||||
struct MidiChannel_PcSpk : public MidiChannel {
|
||||
MidiDriver *device() override;
|
||||
byte getNumber() override;
|
||||
class MidiChannel_PcSpk: public MidiChannel {
|
||||
public:
|
||||
MidiChannel_PcSpk(IMuseDriver_PCSpk *owner, byte number);
|
||||
MidiDriver *device() override { return _owner; }
|
||||
byte getNumber() override { return _number; }
|
||||
void release() override;
|
||||
|
||||
void send(uint32 b) override;
|
||||
|
@ -118,22 +119,23 @@ private:
|
|||
void priority(byte value) override;
|
||||
void sysEx_customInstrument(uint32 type, const byte *instr) override;
|
||||
|
||||
void init(PcSpkDriver *owner, byte channel);
|
||||
bool allocate();
|
||||
|
||||
PcSpkDriver *_owner;
|
||||
bool _allocated;
|
||||
byte _channel;
|
||||
|
||||
OutputChannel _out;
|
||||
uint8 _instrument[23];
|
||||
uint8 _programNr;
|
||||
uint8 _priority;
|
||||
uint8 _tl;
|
||||
uint8 _modWheel;
|
||||
int16 _pitchBend;
|
||||
|
||||
private:
|
||||
IMuseDriver_PCSpk *_owner;
|
||||
const byte _number;
|
||||
uint8 _programNr;
|
||||
uint8 _sustain;
|
||||
uint8 _pitchBendFactor;
|
||||
int16 _pitchBend;
|
||||
|
||||
};
|
||||
|
||||
void setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data);
|
||||
|
@ -142,7 +144,7 @@ private:
|
|||
void updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def);
|
||||
uint8 advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def);
|
||||
|
||||
MidiChannel_PcSpk _channels[6];
|
||||
MidiChannel_PcSpk *_channels[6];
|
||||
MidiChannel_PcSpk *_activeChannel;
|
||||
|
||||
MidiChannel_PcSpk *_lastActiveChannel;
|
||||
|
|
|
@ -1437,17 +1437,6 @@ int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi, MidiDriver
|
|||
init_queue();
|
||||
init_parts();
|
||||
|
||||
if (_midi_native && _soundType != MDT_MACINTOSH && _soundType != MDT_AMIGA) {
|
||||
if (_native_mt32 && !_enable_gs) {
|
||||
initMT32(_midi_native);
|
||||
} else if (_enable_gs) {
|
||||
initGS(_midi_native);
|
||||
} else if (!_native_mt32) {
|
||||
// If GS is disabled we do the "normal" init from the original GM drivers.
|
||||
initGM();
|
||||
}
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
|
||||
return 0;
|
||||
|
@ -1463,174 +1452,6 @@ void IMuseInternal::initMidiDriver(TimerCallbackInfo *info) {
|
|||
info->driver->setTimerCallback(info, &IMuseInternal::midiTimerCallback);
|
||||
}
|
||||
|
||||
void IMuseInternal::initMT32(MidiDriver *midi) {
|
||||
byte buffer[52];
|
||||
|
||||
// Reset the MT-32
|
||||
midi->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
|
||||
_system->delayMillis(250);
|
||||
|
||||
// Setup master tune, reverb mode, reverb time, reverb level,
|
||||
// channel mapping, partial reserve and master volume
|
||||
midi->sysEx((const byte *) "\x41\x10\x16\x12\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 31);
|
||||
_system->delayMillis(250);
|
||||
|
||||
// Map percussion to notes 24 - 34 without reverb
|
||||
midi->sysEx((const byte *) "\x41\x10\x16\x12\x03\x01\x10\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00\x44", 52);
|
||||
_system->delayMillis(250);
|
||||
|
||||
// Compute version string (truncated to 20 chars max.)
|
||||
Common::String infoStr = "ScummVM ";
|
||||
infoStr += gScummVMVersion;
|
||||
int len = infoStr.size();
|
||||
if (len > 20)
|
||||
len = 20;
|
||||
|
||||
// Display a welcome message on MT-32 displays.
|
||||
memcpy(&buffer[0], "\x41\x10\x16\x12\x20\x00\x00", 7);
|
||||
memcpy(&buffer[7], " ", 20);
|
||||
memcpy(buffer + 7 + (20 - len) / 2, infoStr.c_str(), len);
|
||||
byte checksum = 0;
|
||||
for (int i = 4; i < 27; ++i)
|
||||
checksum -= buffer[i];
|
||||
buffer[27] = checksum & 0x7F;
|
||||
midi->sysEx(buffer, 28);
|
||||
_system->delayMillis(1000);
|
||||
}
|
||||
|
||||
void IMuseInternal::initGS(MidiDriver *midi) {
|
||||
byte buffer[12];
|
||||
int i;
|
||||
|
||||
// General MIDI System On message
|
||||
// Resets all GM devices to default settings
|
||||
memcpy(&buffer[0], "\x7E\x7F\x09\x01", 4);
|
||||
midi->sysEx(buffer, 4);
|
||||
debug(2, "GM SysEx: GM System On");
|
||||
_system->delayMillis(200);
|
||||
|
||||
if (_enable_gs) {
|
||||
// All GS devices recognize the GS Reset command,
|
||||
// even using Roland's ID. It is impractical to
|
||||
// support other manufacturers' devices for
|
||||
// further GS settings, as there are limitless
|
||||
// numbers of them out there that would each
|
||||
// require individual SysEx commands with unique IDs.
|
||||
|
||||
// Roland GS SysEx ID
|
||||
memcpy(&buffer[0], "\x41\x10\x42\x12", 4);
|
||||
|
||||
// GS Reset
|
||||
memcpy(&buffer[4], "\x40\x00\x7F\x00\x41", 5);
|
||||
midi->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: GS Reset");
|
||||
_system->delayMillis(200);
|
||||
|
||||
// Set global Master Tune to 442.0kHz, as on the MT-32
|
||||
memcpy(&buffer[4], "\x40\x00\x00\x00\x04\x04\x0F\x29", 8);
|
||||
midi->sysEx(buffer, 12);
|
||||
debug(2, "GS SysEx: Master Tune set to 442.0kHz");
|
||||
|
||||
// Note: All Roland GS devices support CM-64/32L maps
|
||||
|
||||
// Set Channels 1-16 to SC-55 Map, then CM-64/32L Variation
|
||||
for (i = 0; i < 16; ++i) {
|
||||
midi->send((127 << 16) | (0 << 8) | (0xB0 | i));
|
||||
midi->send((1 << 16) | (32 << 8) | (0xB0 | i));
|
||||
midi->send((0 << 16) | (0 << 8) | (0xC0 | i));
|
||||
}
|
||||
debug(2, "GS Program Change: CM-64/32L Map Selected");
|
||||
|
||||
// Set Percussion Channel to SC-55 Map (CC#32, 01H), then
|
||||
// Switch Drum Map to CM-64/32L (MT-32 Compatible Drums)
|
||||
midi->getPercussionChannel()->controlChange(0, 0);
|
||||
midi->getPercussionChannel()->controlChange(32, 1);
|
||||
midi->send(127 << 8 | 0xC0 | 9);
|
||||
debug(2, "GS Program Change: Drum Map is CM-64/32L");
|
||||
|
||||
// Set Master Chorus to 0. The MT-32 has no chorus capability.
|
||||
memcpy(&buffer[4], "\x40\x01\x3A\x00\x05", 5);
|
||||
midi->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Master Chorus Level is 0");
|
||||
|
||||
// Set Channels 1-16 Reverb to 64, which is the
|
||||
// equivalent of MT-32 default Reverb Level 5
|
||||
for (i = 0; i < 16; ++i)
|
||||
midi->send((64 << 16) | (91 << 8) | (0xB0 | i));
|
||||
debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64");
|
||||
|
||||
// Set Channels 1-16 Pitch Bend Sensitivity to
|
||||
// 12 semitones; then lock the RPN by setting null.
|
||||
for (i = 0; i < 16; ++i) {
|
||||
midi->setPitchBendRange(i, 12);
|
||||
}
|
||||
debug(2, "GM Controller 6 Change: Channels 1-16 Pitch Bend Sensitivity is 12 semitones");
|
||||
|
||||
// Set channels 1-16 Mod. LFO1 Pitch Depth to 4
|
||||
memcpy(&buffer[4], "\x40\x20\x04\x04\x18", 5);
|
||||
for (i = 0; i < 16; ++i) {
|
||||
buffer[5] = 0x20 + i;
|
||||
buffer[8] = 0x18 - i;
|
||||
midi->sysEx(buffer, 9);
|
||||
}
|
||||
debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4");
|
||||
|
||||
// Set Percussion Channel Expression to 80
|
||||
midi->getPercussionChannel()->controlChange(11, 80);
|
||||
debug(2, "GM Controller 11 Change: Percussion Channel Expression Level is 80");
|
||||
|
||||
// Turn off Percussion Channel Rx. Expression so that
|
||||
// Expression cannot be modified. I don't know why, but
|
||||
// Roland does it this way.
|
||||
memcpy(&buffer[4], "\x40\x10\x0E\x00\x22", 5);
|
||||
midi->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Percussion Channel Rx. Expression is OFF");
|
||||
|
||||
// Change Reverb Character to 0. I don't think this
|
||||
// sounds most like MT-32, but apparently Roland does.
|
||||
memcpy(&buffer[4], "\x40\x01\x31\x00\x0E", 5);
|
||||
midi->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Reverb Character is 0");
|
||||
|
||||
// Change Reverb Pre-LF to 4, which is similar to
|
||||
// what MT-32 reverb does.
|
||||
memcpy(&buffer[4], "\x40\x01\x32\x04\x09", 5);
|
||||
midi->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Reverb Pre-LF is 4");
|
||||
|
||||
// Change Reverb Time to 106; the decay on Hall 2
|
||||
// Reverb is too fast compared to the MT-32's
|
||||
memcpy(&buffer[4], "\x40\x01\x34\x6A\x21", 5);
|
||||
midi->sysEx(buffer, 9);
|
||||
debug(2, "GS SysEx: Reverb Time is 106");
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseInternal::initGM() {
|
||||
// These are the init messages from the DOTT General Midi
|
||||
// driver. This is the major part of the bug fix for bug
|
||||
// no. 13460 ("DOTT: Incorrect MIDI pitch bending").
|
||||
// SAMNMAX has some less of the default settings (since
|
||||
// the driver works a bit different), but it uses the same
|
||||
// value for the pitch bend range.
|
||||
MidiDriver *m = _midi_native;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
m->send(0x0064B0 | i);
|
||||
m->send(0x0065B0 | i);
|
||||
m->send(0x1006B0 | i);
|
||||
m->send(0x7F07B0 | i);
|
||||
m->send(0x3F0AB0 | i);
|
||||
m->send(0x0000C0 | i);
|
||||
m->send(0x4000E0 | i);
|
||||
m->send(0x0001B0 | i);
|
||||
m->send(0x0040B0 | i);
|
||||
m->send(0x405BB0 | i);
|
||||
m->send(0x005DB0 | i);
|
||||
m->send(0x0000B0 | i);
|
||||
m->send(0x007BB0 | i);
|
||||
}
|
||||
}
|
||||
|
||||
void IMuseInternal::init_queue() {
|
||||
_queue_adding = false;
|
||||
_queue_pos = 0;
|
||||
|
|
|
@ -367,6 +367,7 @@ struct Part : public Common::Serializable {
|
|||
void set_pri(int8 pri);
|
||||
void set_pan(int8 pan);
|
||||
|
||||
void set_sm17(int8 val);
|
||||
void set_onoff(bool on);
|
||||
void fix_after_load();
|
||||
|
||||
|
@ -481,9 +482,6 @@ protected:
|
|||
void handle_marker(uint id, byte data);
|
||||
int get_channel_volume(uint a);
|
||||
void initMidiDriver(TimerCallbackInfo *info);
|
||||
void initGS(MidiDriver *midi);
|
||||
void initMT32(MidiDriver *midi);
|
||||
void initGM();
|
||||
void init_players();
|
||||
void init_parts();
|
||||
void init_queue();
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include "common/debug.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
#include "scumm/imuse/imuse_internal.h"
|
||||
#include "scumm/scumm.h"
|
||||
#include "scumm/imuse/imuse_internal.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
|
@ -146,6 +146,10 @@ void Part::set_pan(int8 pan) {
|
|||
sendPanPosition(_pan_eff + 0x40);
|
||||
}
|
||||
|
||||
void Part::set_sm17(int8 val) {
|
||||
|
||||
}
|
||||
|
||||
void Part::set_transpose(int8 transpose, int8 clipRangeLow, int8 clipRangeHi) {
|
||||
if (_se->_game_id == GID_TENTACLE && (transpose > 24 || transpose < -24))
|
||||
return;
|
||||
|
|
|
@ -223,7 +223,7 @@ int Player::start_seq_sound(int sound, bool reset_vars) {
|
|||
}
|
||||
|
||||
void Player::loadStartParameters(int sound) {
|
||||
_priority = 0x80;
|
||||
_priority = (_se->_game_id != GID_SAMNMAX) ? 0x80 : 0;
|
||||
_volume = 0x7F;
|
||||
_vol_chan = 0xFFFF;
|
||||
_vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;
|
||||
|
@ -319,10 +319,15 @@ void Player::send(uint32 b) {
|
|||
part->pitchBendFactor(param2);
|
||||
break;
|
||||
case 17: // GP Slider 2
|
||||
if (_se->_game_id == GID_SAMNMAX)
|
||||
part->set_sm17(param2);
|
||||
else
|
||||
part->set_detune(param2 - 0x40);
|
||||
break;
|
||||
case 18: // GP Slider 3
|
||||
part->set_pri(param2 - 0x40);
|
||||
if (_se->_game_id != GID_SAMNMAX)
|
||||
param2 -= 0x40;
|
||||
part->set_pri(param2);
|
||||
_se->reallocateMidiChannels(_midi);
|
||||
break;
|
||||
case 64: // Sustain Pedal
|
||||
|
@ -401,9 +406,6 @@ void Player::sysEx(const byte *p, uint16 len) {
|
|||
part->_instrument.send(part->_mc);
|
||||
}
|
||||
}
|
||||
} else if (a == YM2612_SYSEX_ID) {
|
||||
// FM-TOWNS custom instrument definition
|
||||
_midi->sysEx_customInstrument(p[0], 'EUP ', p + 1);
|
||||
} else {
|
||||
// SysEx manufacturer 0x97 has been spotted in the
|
||||
// Monkey Island 2 AdLib music, so don't make this a
|
||||
|
|
|
@ -33,7 +33,9 @@ MODULE_OBJS := \
|
|||
imuse/sysex_scumm.o \
|
||||
imuse/drivers/amiga.o \
|
||||
imuse/drivers/fmtowns.o \
|
||||
imuse/drivers/gmidi.o \
|
||||
imuse/drivers/mac_m68k.o \
|
||||
imuse/drivers/mt32.o \
|
||||
imuse/drivers/pcspk.o \
|
||||
input.o \
|
||||
ks_check.o \
|
||||
|
|
|
@ -81,6 +81,8 @@
|
|||
#include "scumm/imuse/drivers/mac_m68k.h"
|
||||
#include "scumm/imuse/drivers/amiga.h"
|
||||
#include "scumm/imuse/drivers/fmtowns.h"
|
||||
#include "scumm/imuse/drivers/gmidi.h"
|
||||
#include "scumm/imuse/drivers/mt32.h"
|
||||
#include "scumm/detection_steam.h"
|
||||
|
||||
#include "backends/audiocd/audiocd.h"
|
||||
|
@ -2044,10 +2046,16 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
|
|||
bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB);
|
||||
bool useOnlyNative = false;
|
||||
|
||||
uint32 imsFlags = 0;
|
||||
if (_native_mt32)
|
||||
imsFlags |= IMuse::kFlagNativeMT32;
|
||||
if (enable_gs && MidiDriver::getMusicType(dev) != MT_MT32)
|
||||
imsFlags |= IMuse::kFlagRolandGS;
|
||||
|
||||
if (isMacM68kIMuse()) {
|
||||
// We setup this driver as native MIDI driver to avoid playback
|
||||
// of the Mac music via a selected MIDI device.
|
||||
nativeMidiDriver = new MacM68kDriver(_mixer);
|
||||
nativeMidiDriver = new IMuseDriver_MacM68k(_mixer);
|
||||
// The Mac driver is never MT-32.
|
||||
_native_mt32 = false;
|
||||
// Ignore non-native drivers. This also ignores the multi MIDI setting.
|
||||
|
@ -2057,31 +2065,25 @@ void ScummEngine::setupMusic(int midi, const Common::String &macInstrumentFile)
|
|||
_native_mt32 = enable_gs = false;
|
||||
useOnlyNative = true;
|
||||
} else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) {
|
||||
nativeMidiDriver = MidiDriver::createMidi(dev);
|
||||
if (_native_mt32)
|
||||
nativeMidiDriver = new IMuseDriver_MT32(dev, _game.id == GID_SAMNMAX);
|
||||
else
|
||||
nativeMidiDriver = new IMuseDriver_GMidi(dev, imsFlags & IMuse::kFlagRolandGS, _game.id == GID_SAMNMAX);
|
||||
}
|
||||
|
||||
if (nativeMidiDriver != nullptr && _native_mt32)
|
||||
nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
||||
|
||||
if (!useOnlyNative) {
|
||||
if (_sound->_musicType == MDT_TOWNS) {
|
||||
adlibMidiDriver = new MidiDriver_TOWNS(_mixer);
|
||||
} else if (_sound->_musicType == MDT_ADLIB || multi_midi) {
|
||||
adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB));
|
||||
if (_sound->_musicType == MDT_ADLIB || multi_midi) {
|
||||
adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(MDT_ADLIB));
|
||||
adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
|
||||
// Try to use OPL3 mode for Sam&Max when possible.
|
||||
adlibMidiDriver->property(MidiDriver::PROP_SCUMM_OPL3, (_game.id == GID_SAMNMAX) ? 1 : 0);
|
||||
} else if (_sound->_musicType == MDT_TOWNS) {
|
||||
adlibMidiDriver = new IMuseDriver_FMTowns(_mixer);
|
||||
} else if (_sound->_musicType == MDT_PCSPK) {
|
||||
adlibMidiDriver = new PcSpkDriver(_mixer);
|
||||
adlibMidiDriver = new IMuseDriver_PCSpk(_mixer);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 imsFlags = 0;
|
||||
if (_native_mt32)
|
||||
imsFlags |= IMuse::kFlagNativeMT32;
|
||||
if (enable_gs && MidiDriver::getMusicType(dev) != MT_MT32)
|
||||
imsFlags |= IMuse::kFlagRolandGS;
|
||||
|
||||
_imuse = IMuse::create(this, nativeMidiDriver, adlibMidiDriver, isMacM68kIMuse() ? MDT_MACINTOSH : _sound->_musicType, imsFlags);
|
||||
|
||||
if (_game.platform == Common::kPlatformFMTowns) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue