Merge pull request #54 "SCUMM v5 PC Speaker driver".

Discussion can be found here: https://github.com/scummvm/scummvm/pull/54
This commit is contained in:
Johannes Schickel 2011-07-11 21:51:24 +02:00
commit a8846d37d5
14 changed files with 1114 additions and 32 deletions

View file

@ -243,11 +243,11 @@ static const GameSettings gameVariantsTable[] = {
{"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_TOWNS, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
{"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey2", "", 0, GID_MONKEY2, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
{"monkey2", "FM-TOWNS", 0, GID_MONKEY2, 5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_MIDITOWNS | GUIO_MIDIADLIB | GUIO_MIDIMT32},
{"monkey2", "", 0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
{"monkey2", "FM-TOWNS", 0, GID_MONKEY2, 5, 0, MDT_PCSPK | MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_MIDITOWNS | GUIO_MIDIADLIB | GUIO_MIDIMT32},
{"atlantis", "", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},
{"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
{"atlantis", "", 0, GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},
{"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_PCSPK | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
{"atlantis", "FM-TOWNS", 0, GID_INDY4, 5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_MIDITOWNS | GUIO_MIDIADLIB | GUIO_MIDIMT32},
{"tentacle", "", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NONE},

View file

@ -64,7 +64,8 @@ _queue_cleared(0),
_master_volume(0),
_music_volume(0),
_trigger_count(0),
_snm_trigger_index(0) {
_snm_trigger_index(0),
_pcSpeaker(false) {
memset(_channel_volume,0,sizeof(_channel_volume));
memset(_channel_volume_eff,0,sizeof(_channel_volume_eff));
memset(_volchan_table,0,sizeof(_volchan_table));
@ -467,6 +468,10 @@ uint32 IMuseInternal::property(int prop, uint32 value) {
case IMuse::PROP_GAME_ID:
_game_id = value;
break;
case IMuse::PROP_PC_SPEAKER:
_pcSpeaker = (value != 0);
break;
}
return 0;
@ -1668,16 +1673,19 @@ void IMuseInternal::reallocateMidiChannels(MidiDriver *midi) {
}
}
void IMuseInternal::setGlobalAdLibInstrument(byte slot, byte *data) {
void IMuseInternal::setGlobalInstrument(byte slot, byte *data) {
if (slot < 32) {
_global_adlib_instruments[slot].adlib(data);
if (_pcSpeaker)
_global_instruments[slot].pcspk(data);
else
_global_instruments[slot].adlib(data);
}
}
void IMuseInternal::copyGlobalAdLibInstrument(byte slot, Instrument *dest) {
void IMuseInternal::copyGlobalInstrument(byte slot, Instrument *dest) {
if (slot >= 32)
return;
_global_adlib_instruments[slot].copy_to(dest);
_global_instruments[slot].copy_to(dest);
}

View file

@ -55,7 +55,8 @@ public:
PROP_GS,
PROP_LIMIT_PLAYERS,
PROP_RECYCLE_PLAYERS,
PROP_GAME_ID
PROP_GAME_ID,
PROP_PC_SPEAKER
};
public:

View file

@ -342,6 +342,7 @@ struct Part : public Serializable {
void off();
void set_instrument(uint b);
void set_instrument(byte *data);
void set_instrument_pcspk(byte *data);
void load_global_instrument(byte b);
void set_transpose(int8 transpose);
@ -433,7 +434,8 @@ protected:
Player _players[8];
Part _parts[32];
Instrument _global_adlib_instruments[32];
bool _pcSpeaker;
Instrument _global_instruments[32];
CommandQueue _cmd_queue[64];
DeferredCommand _deferredCommands[4];
@ -498,8 +500,8 @@ protected:
int setImuseMasterVolume(uint vol);
void reallocateMidiChannels(MidiDriver *midi);
void setGlobalAdLibInstrument(byte slot, byte *data);
void copyGlobalAdLibInstrument(byte slot, Instrument *dest);
void setGlobalInstrument(byte slot, byte *data);
void copyGlobalInstrument(byte slot, Instrument *dest);
bool isNativeMT32() { return _native_mt32; }
protected:

View file

@ -194,13 +194,17 @@ void Part::set_onoff(bool on) {
}
void Part::set_instrument(byte * data) {
_instrument.adlib(data);
if (_se->_pcSpeaker)
_instrument.pcspk(data);
else
_instrument.adlib(data);
if (clearToTransmit())
_instrument.send(_mc);
}
void Part::load_global_instrument(byte slot) {
_player->_se->copyGlobalAdLibInstrument(slot, &_instrument);
_player->_se->copyGlobalInstrument(slot, &_instrument);
if (clearToTransmit())
_instrument.send(_mc);
}

View file

@ -371,11 +371,13 @@ void Player::sysEx(const byte *p, uint16 len) {
if (a != IMUSE_SYSEX_ID) {
if (a == ROLAND_SYSEX_ID) {
// Roland custom instrument definition.
part = getPart(p[0] & 0x0F);
if (part) {
part->_instrument.roland(p - 1);
if (part->clearToTransmit())
part->_instrument.send(part->_mc);
if (_isMIDI || _isMT32) {
part = getPart(p[0] & 0x0F);
if (part) {
part->_instrument.roland(p - 1);
if (part->clearToTransmit())
part->_instrument.send(part->_mc);
}
}
} else if (a == YM2612_SYSEX_ID) {
// FM-TOWNS custom instrument definition

View file

@ -259,6 +259,19 @@ public:
bool is_valid() { return (_native_mt32 ? true : (_instrument_name[0] != '\0')); }
};
class Instrument_PcSpk : public InstrumentInternal {
public:
Instrument_PcSpk(const byte *data);
Instrument_PcSpk(Serializer *s);
void saveOrLoad(Serializer *s);
void send(MidiChannel *mc);
void copy_to(Instrument *dest) { dest->pcspk((byte *)&_instrument); }
bool is_valid() { return true; }
private:
byte _instrument[23];
};
////////////////////////////////////////
//
// Instrument class members
@ -299,6 +312,14 @@ void Instrument::roland(const byte *instrument) {
_instrument = new Instrument_Roland(instrument);
}
void Instrument::pcspk(const byte *instrument) {
clear();
if (!instrument)
return;
_type = itPcSpk;
_instrument = new Instrument_PcSpk(instrument);
}
void Instrument::saveOrLoad (Serializer *s) {
if (s->isSaving()) {
s->saveByte(_type);
@ -319,6 +340,9 @@ void Instrument::saveOrLoad (Serializer *s) {
case itRoland:
_instrument = new Instrument_Roland(s);
break;
case itPcSpk:
_instrument = new Instrument_PcSpk(s);
break;
default:
warning("No known instrument classification #%d", (int)_type);
_type = itNone;
@ -470,4 +494,32 @@ uint8 Instrument_Roland::getEquivalentGM() {
return 255;
}
////////////////////////////////////////
//
// Instrument_PcSpk class members
//
////////////////////////////////////////
Instrument_PcSpk::Instrument_PcSpk(const byte *data) {
memcpy(_instrument, data, sizeof(_instrument));
}
Instrument_PcSpk::Instrument_PcSpk(Serializer *s) {
if (!s->isSaving())
saveOrLoad(s);
else
memset(_instrument, 0, sizeof(_instrument));
}
void Instrument_PcSpk::saveOrLoad(Serializer *s) {
if (s->isSaving())
s->saveBytes(_instrument, sizeof(_instrument));
else
s->loadBytes(_instrument, sizeof(_instrument));
}
void Instrument_PcSpk::send(MidiChannel *mc) {
mc->sysEx_customInstrument('SPK ', (byte *)&_instrument);
}
} // End of namespace Scumm

View file

@ -51,7 +51,8 @@ public:
itNone = 0,
itProgram = 1,
itAdLib = 2,
itRoland = 3
itRoland = 3,
itPcSpk = 4
};
Instrument() : _type (0), _instrument (0) { }
@ -70,6 +71,7 @@ public:
void program(byte program, bool mt32);
void adlib(const byte *instrument);
void roland(const byte *instrument);
void pcspk(const byte *instrument);
byte getType() { return _type; }
bool isValid() { return (_instrument ? _instrument->is_valid() : false); }

View file

@ -0,0 +1,835 @@
/* 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 "scumm/imuse/pcspk.h"
#include "common/util.h"
namespace Scumm {
PcSpkDriver::PcSpkDriver(Audio::Mixer *mixer)
: MidiDriver_Emulated(mixer), _pcSpk(mixer->getOutputRate()) {
}
PcSpkDriver::~PcSpkDriver() {
close();
}
int PcSpkDriver::open() {
if (_isOpen)
return MERR_ALREADY_OPEN;
MidiDriver_Emulated::open();
for (uint i = 0; i < 6; ++i)
_channels[i].init(this, i);
_activeChannel = 0;
_effectTimer = 0;
_randBase = 1;
// We need to take care we only send note frequencies, when the internal
// settings actually changed, thus we need some extra state to keep track
// of that.
_lastActiveChannel = 0;
_lastActiveOut = 0;
// We set the output sound type to music here to allow sound volume
// adjustment. The drawback here is that we can not control the music and
// sfx separately here. But the AdLib output has the same issue so it
// should not be that bad.
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
return 0;
}
void PcSpkDriver::close() {
if (!_isOpen)
return;
_isOpen = false;
_mixer->stopHandle(_mixerSoundHandle);
}
void PcSpkDriver::send(uint32 d) {
assert((d & 0x0F) < 6);
_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() {
for (uint i = 0; i < 6; ++i) {
if (_channels[i].allocate())
return &_channels[i];
}
return 0;
}
void PcSpkDriver::generateSamples(int16 *buf, int len) {
_pcSpk.readBuffer(buf, len);
}
void PcSpkDriver::onTimer() {
if (!_activeChannel)
return;
for (uint i = 0; i < 6; ++i) {
OutputChannel &out = _channels[i]._out;
if (!out.active)
continue;
if (out.length == 0 || --out.length != 0) {
if (out.unkB && out.unkC) {
out.unkA += out.unkB;
if (out.instrument)
out.unkE = ((int8)out.instrument[out.unkA] * out.unkC) >> 4;
}
++_effectTimer;
if (_effectTimer > 3) {
_effectTimer = 0;
if (out.effectEnvelopeA.state)
updateEffectGenerator(_channels[i], out.effectEnvelopeA, out.effectDefA);
if (out.effectEnvelopeB.state)
updateEffectGenerator(_channels[i], out.effectEnvelopeB, out.effectDefB);
}
} else {
out.active = 0;
updateNote();
return;
}
}
if (_activeChannel->_tl) {
output((_activeChannel->_out.note << 7) + _activeChannel->_pitchBend + _activeChannel->_out.unk60 + _activeChannel->_out.unkE);
} else {
_pcSpk.stop();
_lastActiveChannel = 0;
_lastActiveOut = 0;
}
}
void PcSpkDriver::updateNote() {
uint8 priority = 0;
_activeChannel = 0;
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 (_activeChannel == 0 || _activeChannel->_tl == 0) {
_pcSpk.stop();
_lastActiveChannel = 0;
_lastActiveOut = 0;
} else {
output(_activeChannel->_pitchBend + (_activeChannel->_out.note << 7));
}
}
void PcSpkDriver::output(uint16 out) {
byte v1 = (out >> 7) & 0xFF;
byte v2 = (out >> 2) & 0x1E;
byte shift = _outputTable1[v1];
uint16 indexBase = _outputTable2[v1] << 5;
uint16 frequency = _frequencyTable[(indexBase + v2) / 2] >> shift;
// Only output in case the active channel changed or the frequency changed.
// This is not faithful to the original. Since our timings differ we would
// get distorted sound otherwise though.
if (_lastActiveChannel != _activeChannel || _lastActiveOut != out) {
_pcSpk.play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / frequency, -1);
_lastActiveChannel = _activeChannel;
_lastActiveOut = out;
}
}
void PcSpkDriver::MidiChannel_PcSpk::init(PcSpkDriver *owner, byte channel) {
_owner = owner;
_channel = channel;
_allocated = false;
memset(&_out, 0, sizeof(_out));
}
bool PcSpkDriver::MidiChannel_PcSpk::allocate() {
if (_allocated)
return false;
memset(&_out, 0, sizeof(_out));
memset(_instrument, 0, sizeof(_instrument));
_out.effectDefA.envelope = &_out.effectEnvelopeA;
_out.effectDefB.envelope = &_out.effectEnvelopeB;
_allocated = true;
return true;
}
MidiDriver *PcSpkDriver::MidiChannel_PcSpk::device() {
return _owner;
}
byte PcSpkDriver::MidiChannel_PcSpk::getNumber() {
return _channel;
}
void PcSpkDriver::MidiChannel_PcSpk::release() {
_out.active = 0;
_allocated = false;
_owner->updateNote();
}
void PcSpkDriver::MidiChannel_PcSpk::send(uint32 b) {
uint8 type = b & 0xF0;
uint8 p1 = (b >> 8) & 0xFF;
uint8 p2 = (b >> 16) & 0xFF;
switch (type) {
case 0x80:
noteOff(p1);
break;
case 0x90:
if (p2)
noteOn(p1, p2);
else
noteOff(p1);
break;
case 0xB0:
controlChange(p1, p2);
break;
case 0xE0:
pitchBend((p1 | (p2 << 7)) - 0x2000);
break;
default:
break;
}
}
void PcSpkDriver::MidiChannel_PcSpk::noteOff(byte note) {
if (!_allocated)
return;
if (_sustain) {
if (_out.note == note)
_out.sustainNoteOff = 1;
} else {
if (_out.note == note) {
_out.active = 0;
_owner->updateNote();
}
}
}
void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) {
if (!_allocated)
return;
_out.note = note;
_out.sustainNoteOff = 0;
_out.length = _instrument[0];
if (_instrument[4] * 256 < ARRAYSIZE(PcSpkDriver::_outInstrumentData))
_out.instrument = _owner->_outInstrumentData + _instrument[4] * 256;
else
_out.instrument = 0;
_out.unkA = 0;
_out.unkB = _instrument[1];
_out.unkC = _instrument[2];
_out.unkE = 0;
_out.unk60 = 0;
_out.active = 1;
// In case we get a note on event on the last active channel, we reset the
// last active channel, thus we assure the frequency is correctly set, even
// when the same note was sent.
if (_owner->_lastActiveChannel == this) {
_owner->_lastActiveChannel = 0;
_owner->_lastActiveOut = 0;
}
_owner->updateNote();
_out.unkC += PcSpkDriver::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4));
if (_out.unkC > 63)
_out.unkC = 63;
if ((_instrument[5] & 0x80) != 0)
_owner->setupEffects(*this, _out.effectEnvelopeA, _out.effectDefA, _instrument[5], _instrument + 6);
if ((_instrument[14] & 0x80) != 0)
_owner->setupEffects(*this, _out.effectEnvelopeB, _out.effectDefB, _instrument[14], _instrument + 15);
}
void PcSpkDriver::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) {
_pitchBend = (bend * _pitchBendFactor) >> 6;
}
void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) {
switch (control) {
case 1:
if (_out.effectEnvelopeA.state && _out.effectDefA.useModWheel)
_out.effectEnvelopeA.modWheelState = (value >> 2);
if (_out.effectEnvelopeB.state && _out.effectDefB.useModWheel)
_out.effectEnvelopeB.modWheelState = (value >> 2);
break;
case 7:
_tl = value;
if (_owner->_activeChannel == this) {
if (_tl == 0) {
_owner->_lastActiveChannel = 0;
_owner->_lastActiveOut = 0;
_owner->_pcSpk.stop();
} else {
_owner->output((_out.note << 7) + _pitchBend + _out.unk60 + _out.unkE);
}
}
break;
case 64:
_sustain = value;
if (!value && _out.sustainNoteOff) {
_out.active = 0;
_owner->updateNote();
}
break;
case 123:
_out.active = 0;
_owner->updateNote();
break;
default:
break;
}
}
void PcSpkDriver::MidiChannel_PcSpk::pitchBendFactor(byte value) {
_pitchBendFactor = value;
}
void PcSpkDriver::MidiChannel_PcSpk::priority(byte value) {
_priority = value;
}
void PcSpkDriver::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const byte *instr) {
memcpy(_instrument, instr, sizeof(_instrument));
}
uint8 PcSpkDriver::getEffectModifier(uint16 level) {
uint8 base = level / 32;
uint8 index = level % 32;
if (index == 0)
return 0;
return (base * (index + 1)) >> 5;
}
int16 PcSpkDriver::getEffectModLevel(int16 level, int8 mod) {
if (!mod) {
return 0;
} else if (mod == 31) {
return level;
} else if (level < -63 || level > 63) {
return (mod * (level + 1)) >> 6;
} else if (mod < 0) {
if (level < 0)
return getEffectModifier(((-level) << 5) - mod);
else
return -getEffectModifier((level << 5) - mod);
} else {
if (level < 0)
return -getEffectModifier(((-level) << 5) + mod);
else
return getEffectModifier(((-level) << 5) + mod);
}
}
int16 PcSpkDriver::getRandMultipy(int16 input) {
if (_randBase & 1)
_randBase = (_randBase >> 1) ^ 0xB8;
else
_randBase >>= 1;
return (_randBase * input) >> 8;
}
void PcSpkDriver::setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data) {
def.phase = 0;
def.useModWheel = flags & 0x40;
env.loop = flags & 0x20;
def.type = flags & 0x1F;
env.modWheelSensitivity = 31;
if (def.useModWheel)
env.modWheelState = chan._modWheel >> 2;
else
env.modWheelState = 31;
switch (def.type) {
case 0:
env.maxLevel = 767;
env.startLevel = 383;
break;
case 1:
env.maxLevel = 31;
env.startLevel = 15;
break;
case 2:
env.maxLevel = 63;
env.startLevel = chan._out.unkB;
break;
case 3:
env.maxLevel = 63;
env.startLevel = chan._out.unkC;
break;
case 4:
env.maxLevel = 3;
env.startLevel = chan._instrument[4];
break;
case 5:
env.maxLevel = 62;
env.startLevel = 31;
env.modWheelState = 0;
break;
case 6:
env.maxLevel = 31;
env.startLevel = 0;
env.modWheelSensitivity = 0;
break;
default:
break;
}
startEffect(env, data);
}
void PcSpkDriver::startEffect(EffectEnvelope &env, const byte *data) {
env.state = 1;
env.currentLevel = 0;
env.modWheelLast = 31;
env.duration = data[0] * 63;
env.stateTargetLevels[0] = data[1];
env.stateTargetLevels[1] = data[3];
env.stateTargetLevels[2] = data[5];
env.stateTargetLevels[3] = data[6];
env.stateModWheelLevels[0] = data[2];
env.stateModWheelLevels[1] = data[4];
env.stateModWheelLevels[2] = 0;
env.stateModWheelLevels[3] = data[7];
initNextEnvelopeState(env);
}
void PcSpkDriver::initNextEnvelopeState(EffectEnvelope &env) {
uint8 lastState = env.state - 1;
uint16 stepCount = _effectEnvStepTable[getEffectModifier(((env.stateTargetLevels[lastState] & 0x7F) << 5) + env.modWheelSensitivity)];
if (env.stateTargetLevels[lastState] & 0x80)
stepCount = getRandMultipy(stepCount);
if (!stepCount)
stepCount = 1;
env.stateNumSteps = env.stateStepCounter = stepCount;
int16 totalChange = 0;
if (lastState != 2) {
totalChange = getEffectModLevel(env.maxLevel, (env.stateModWheelLevels[lastState] & 0x7F) - 31);
if (env.stateModWheelLevels[lastState] & 0x80)
totalChange = getRandMultipy(totalChange);
if (totalChange + env.startLevel > env.maxLevel)
totalChange = env.maxLevel - env.startLevel;
else if (totalChange + env.startLevel < 0)
totalChange = -env.startLevel;
totalChange -= env.currentLevel;
}
env.changePerStep = totalChange / stepCount;
if (totalChange < 0) {
totalChange = -totalChange;
env.dir = -1;
} else {
env.dir = 1;
}
env.changePerStepRem = totalChange % stepCount;
env.changeCountRem = 0;
}
void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def) {
if (advanceEffectEnvelope(env, def) & 1) {
switch (def.type) {
case 0: case 1:
chan._out.unk60 = def.phase << 4;
break;
case 2:
chan._out.unkB = (def.phase & 0xFF) + chan._instrument[1];
break;
case 3:
chan._out.unkC = (def.phase & 0xFF) + chan._instrument[2];
break;
case 4:
if ((chan._instrument[4] + (def.phase & 0xFF)) * 256 < ARRAYSIZE(_outInstrumentData))
chan._out.instrument = _outInstrumentData + (chan._instrument[4] + (def.phase & 0xFF)) * 256;
else
chan._out.instrument = 0;
break;
case 5:
env.modWheelState = (def.phase & 0xFF);
break;
case 6:
env.modWheelSensitivity = (def.phase & 0xFF);
break;
default:
break;
}
}
}
uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) {
if (env.duration != 0) {
env.duration -= 17;
if (env.duration <= 0) {
env.state = 0;
return 0;
}
}
uint8 changedFlags = 0;
int16 newLevel = env.currentLevel + env.changePerStep;
env.changeCountRem += env.changePerStepRem;
if (env.changeCountRem >= env.stateNumSteps) {
env.changeCountRem -= env.stateNumSteps;
newLevel += env.dir;
}
if (env.currentLevel != newLevel || env.modWheelLast != env.modWheelState) {
env.currentLevel = newLevel;
env.modWheelLast = env.modWheelState;
int16 newPhase = getEffectModLevel(newLevel, env.modWheelState);
if (def.phase != newPhase) {
changedFlags |= 1;
def.phase = newPhase;
}
}
--env.stateStepCounter;
if (!env.stateStepCounter) {
++env.state;
if (env.state > 4) {
if (env.loop) {
env.state = 1;
changedFlags |= 2;
} else {
env.state = 0;
return changedFlags;
}
}
initNextEnvelopeState(env);
}
return changedFlags;
}
const byte PcSpkDriver::_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,
0x46, 0x49, 0x4B, 0x4E, 0x50, 0x52, 0x55, 0x57,
0x59, 0x5B, 0x5E, 0x60, 0x62, 0x64, 0x66, 0x67,
0x69, 0x6B, 0x6C, 0x6E, 0x70, 0x71, 0x72, 0x74,
0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7B,
0x7C, 0x7D, 0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E,
0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7D, 0x7D,
0x7C, 0x7B, 0x7B, 0x7A, 0x79, 0x78, 0x77, 0x76,
0x75, 0x74, 0x72, 0x71, 0x70, 0x6E, 0x6C, 0x6B,
0x69, 0x67, 0x66, 0x64, 0x62, 0x60, 0x5E, 0x5B,
0x59, 0x57, 0x55, 0x52, 0x50, 0x4E, 0x4B, 0x49,
0x46, 0x43, 0x41, 0x3E, 0x3B, 0x39, 0x36, 0x33,
0x30, 0x2D, 0x2A, 0x27, 0x24, 0x21, 0x1E, 0x1B,
0x18, 0x15, 0x12, 0x0F, 0x0C, 0x09, 0x06, 0x03,
0x00, 0xFD, 0xFA, 0xF7, 0xF4, 0xF1, 0xEE, 0xEB,
0xE8, 0xE5, 0xE2, 0xDF, 0xDC, 0xD9, 0xD6, 0xD3,
0xD0, 0xCD, 0xCA, 0xC7, 0xC5, 0xC2, 0xBF, 0xBD,
0xBA, 0xB7, 0xB5, 0xB2, 0xB0, 0xAE, 0xAB, 0xA9,
0xA7, 0xA5, 0xA2, 0xA0, 0x9E, 0x9C, 0x9A, 0x99,
0x97, 0x95, 0x94, 0x92, 0x90, 0x8F, 0x8E, 0x8C,
0x8B, 0x8A, 0x89, 0x88, 0x87, 0x86, 0x85, 0x85,
0x84, 0x83, 0x83, 0x82, 0x82, 0x82, 0x82, 0x82,
0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83,
0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A,
0x8B, 0x8C, 0x8E, 0x8F, 0x90, 0x92, 0x94, 0x95,
0x97, 0x99, 0x9A, 0x9C, 0x9E, 0xA0, 0xA2, 0xA5,
0xA7, 0xA9, 0xAB, 0xAE, 0xB0, 0xB2, 0xB5, 0xB7,
0xBA, 0xBD, 0xBF, 0xC2, 0xC5, 0xC7, 0xCA, 0xCD,
0xD0, 0xD3, 0xD6, 0xD9, 0xDC, 0xDF, 0xE2, 0xE5,
0xE8, 0xEB, 0xEE, 0xF1, 0xF4, 0xF7, 0xFA, 0xFD,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
0x29, 0x23, 0xBE, 0x84, 0xE1, 0x6C, 0xD6, 0xAE,
0x52, 0x90, 0x49, 0xF1, 0xF1, 0xBB, 0xE9, 0xEB,
0xB3, 0xA6, 0xDB, 0x3C, 0x87, 0x0C, 0x3E, 0x99,
0x24, 0x5E, 0x0D, 0x1C, 0x06, 0xB7, 0x47, 0xDE,
0xB3, 0x12, 0x4D, 0xC8, 0x43, 0xBB, 0x8B, 0xA6,
0x1F, 0x03, 0x5A, 0x7D, 0x09, 0x38, 0x25, 0x1F,
0x5D, 0xD4, 0xCB, 0xFC, 0x96, 0xF5, 0x45, 0x3B,
0x13, 0x0D, 0x89, 0x0A, 0x1C, 0xDB, 0xAE, 0x32,
0x20, 0x9A, 0x50, 0xEE, 0x40, 0x78, 0x36, 0xFD,
0x12, 0x49, 0x32, 0xF6, 0x9E, 0x7D, 0x49, 0xDC,
0xAD, 0x4F, 0x14, 0xF2, 0x44, 0x40, 0x66, 0xD0,
0x6B, 0xC4, 0x30, 0xB7, 0x32, 0x3B, 0xA1, 0x22,
0xF6, 0x22, 0x91, 0x9D, 0xE1, 0x8B, 0x1F, 0xDA,
0xB0, 0xCA, 0x99, 0x02, 0xB9, 0x72, 0x9D, 0x49,
0x2C, 0x80, 0x7E, 0xC5, 0x99, 0xD5, 0xE9, 0x80,
0xB2, 0xEA, 0xC9, 0xCC, 0x53, 0xBF, 0x67, 0xD6,
0xBF, 0x14, 0xD6, 0x7E, 0x2D, 0xDC, 0x8E, 0x66,
0x83, 0xEF, 0x57, 0x49, 0x61, 0xFF, 0x69, 0x8F,
0x61, 0xCD, 0xD1, 0x1E, 0x9D, 0x9C, 0x16, 0x72,
0x72, 0xE6, 0x1D, 0xF0, 0x84, 0x4F, 0x4A, 0x77,
0x02, 0xD7, 0xE8, 0x39, 0x2C, 0x53, 0xCB, 0xC9,
0x12, 0x1E, 0x33, 0x74, 0x9E, 0x0C, 0xF4, 0xD5,
0xD4, 0x9F, 0xD4, 0xA4, 0x59, 0x7E, 0x35, 0xCF,
0x32, 0x22, 0xF4, 0xCC, 0xCF, 0xD3, 0x90, 0x2D,
0x48, 0xD3, 0x8F, 0x75, 0xE6, 0xD9, 0x1D, 0x2A,
0xE5, 0xC0, 0xF7, 0x2B, 0x78, 0x81, 0x87, 0x44,
0x0E, 0x5F, 0x50, 0x00, 0xD4, 0x61, 0x8D, 0xBE,
0x7B, 0x05, 0x15, 0x07, 0x3B, 0x33, 0x82, 0x1F,
0x18, 0x70, 0x92, 0xDA, 0x64, 0x54, 0xCE, 0xB1,
0x85, 0x3E, 0x69, 0x15, 0xF8, 0x46, 0x6A, 0x04,
0x96, 0x73, 0x0E, 0xD9, 0x16, 0x2F, 0x67, 0x68,
0xD4, 0xF7, 0x4A, 0x4A, 0xD0, 0x57, 0x68, 0x76
};
const byte PcSpkDriver::_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,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7
};
const byte PcSpkDriver::_outputTable2[] = {
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
0, 1, 2, 3,
4, 5, 6, 7
};
const uint16 PcSpkDriver::_effectEnvStepTable[] = {
1, 2, 4, 5,
6, 7, 8, 9,
10, 12, 14, 16,
18, 21, 24, 30,
36, 50, 64, 82,
100, 136, 160, 192,
240, 276, 340, 460,
600, 860, 1200, 1600
};
const uint16 PcSpkDriver::_frequencyTable[] = {
0x8E84, 0x8E00, 0x8D7D, 0x8CFA,
0x8C78, 0x8BF7, 0x8B76, 0x8AF5,
0x8A75, 0x89F5, 0x8976, 0x88F7,
0x8879, 0x87FB, 0x877D, 0x8700,
0x8684, 0x8608, 0x858C, 0x8511,
0x8496, 0x841C, 0x83A2, 0x8328,
0x82AF, 0x8237, 0x81BF, 0x8147,
0x80D0, 0x8059, 0x7FE3, 0x7F6D,
0x7EF7, 0x7E82, 0x7E0D, 0x7D99,
0x7D25, 0x7CB2, 0x7C3F, 0x7BCC,
0x7B5A, 0x7AE8, 0x7A77, 0x7A06,
0x7995, 0x7925, 0x78B5, 0x7846,
0x77D7, 0x7768, 0x76FA, 0x768C,
0x761F, 0x75B2, 0x7545, 0x74D9,
0x746D, 0x7402, 0x7397, 0x732C,
0x72C2, 0x7258, 0x71EF, 0x7186,
0x711D, 0x70B5, 0x704D, 0x6FE5,
0x6F7E, 0x6F17, 0x6EB0, 0x6E4A,
0x6DE5, 0x6D7F, 0x6D1A, 0x6CB5,
0x6C51, 0x6BED, 0x6B8A, 0x6B26,
0x6AC4, 0x6A61, 0x69FF, 0x699D,
0x693C, 0x68DB, 0x687A, 0x681A,
0x67BA, 0x675A, 0x66FA, 0x669B,
0x663D, 0x65DF, 0x6581, 0x6523,
0x64C6, 0x6469, 0x640C, 0x63B0,
0x6354, 0x62F8, 0x629D, 0x6242,
0x61E7, 0x618D, 0x6133, 0x60D9,
0x6080, 0x6027, 0x5FCE, 0x5F76,
0x5F1E, 0x5EC6, 0x5E6E, 0x5E17,
0x5DC1, 0x5D6A, 0x5D14, 0x5CBE,
0x5C68, 0x5C13, 0x5BBE, 0x5B6A,
0x5B15, 0x5AC1, 0x5A6E, 0x5A1A,
0x59C7, 0x5974, 0x5922, 0x58CF,
0x587D, 0x582C, 0x57DA, 0x5789,
0x5739, 0x56E8, 0x5698, 0x5648,
0x55F9, 0x55A9, 0x555A, 0x550B,
0x54BD, 0x546F, 0x5421, 0x53D3,
0x5386, 0x5339, 0x52EC, 0x52A0,
0x5253, 0x5207, 0x51BC, 0x5170,
0x5125, 0x50DA, 0x5090, 0x5046,
0x4FFB, 0x4FB2, 0x4F68, 0x4F1F,
0x4ED6, 0x4E8D, 0x4E45, 0x4DFC,
0x4DB5, 0x4D6D, 0x4D25, 0x4CDE,
0x4C97, 0x4C51, 0x4C0A, 0x4BC4,
0x4B7E, 0x4B39, 0x4AF3, 0x4AAE,
0x4A69, 0x4A24, 0x49E0, 0x499C,
0x4958, 0x4914, 0x48D1, 0x488E,
0x484B, 0x4808, 0x47C6, 0x4783
};
} // End of namespace Scumm

161
engines/scumm/imuse/pcspk.h Normal file
View file

@ -0,0 +1,161 @@
/* 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 SCUMM_IMUSE_PCSPK_H
#define SCUMM_IMUSE_PCSPK_H
#include "audio/softsynth/emumidi.h"
#include "audio/softsynth/pcspk.h"
namespace Scumm {
class PcSpkDriver : public MidiDriver_Emulated {
public:
PcSpkDriver(Audio::Mixer *mixer);
~PcSpkDriver();
virtual int open();
virtual void close();
virtual void send(uint32 d);
virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
virtual MidiChannel *allocateChannel();
virtual MidiChannel *getPercussionChannel() { return 0; }
bool isStereo() const { return _pcSpk.isStereo(); }
int getRate() const { return _pcSpk.getRate(); }
protected:
void generateSamples(int16 *buf, int len);
void onTimer();
private:
Audio::PCSpeaker _pcSpk;
int _effectTimer;
uint8 _randBase;
void updateNote();
void output(uint16 out);
static uint8 getEffectModifier(uint16 level);
int16 getEffectModLevel(int16 level, int8 mod);
int16 getRandMultipy(int16 input);
struct EffectEnvelope {
uint8 state;
int16 currentLevel;
int16 duration;
int16 maxLevel;
int16 startLevel;
uint8 loop;
uint8 stateTargetLevels[4];
uint8 stateModWheelLevels[4];
uint8 modWheelSensitivity;
uint8 modWheelState;
uint8 modWheelLast;
int16 stateNumSteps;
int16 stateStepCounter;
int16 changePerStep;
int8 dir;
int16 changePerStepRem;
int16 changeCountRem;
};
struct EffectDefinition {
int16 phase;
uint8 type;
uint8 useModWheel;
EffectEnvelope *envelope;
};
struct OutputChannel {
uint8 active;
uint8 note;
uint8 sustainNoteOff;
uint8 length;
const uint8 *instrument;
uint8 unkA;
uint8 unkB;
uint8 unkC;
int16 unkE;
EffectEnvelope effectEnvelopeA;
EffectDefinition effectDefA;
EffectEnvelope effectEnvelopeB;
EffectDefinition effectDefB;
int16 unk60;
};
struct MidiChannel_PcSpk : public MidiChannel {
virtual MidiDriver *device();
virtual byte getNumber();
virtual void release();
virtual void send(uint32 b);
virtual void noteOff(byte note);
virtual void noteOn(byte note, byte velocity);
virtual void programChange(byte program);
virtual void pitchBend(int16 bend);
virtual void controlChange(byte control, byte value);
virtual void pitchBendFactor(byte value);
virtual void priority(byte value);
virtual void sysEx_customInstrument(uint32 type, const byte *instr);
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;
uint8 _sustain;
uint8 _pitchBendFactor;
int16 _pitchBend;
};
void setupEffects(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def, byte flags, const byte *data);
void startEffect(EffectEnvelope &env, const byte *data);
void initNextEnvelopeState(EffectEnvelope &env);
void updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope &env, EffectDefinition &def);
uint8 advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def);
MidiChannel_PcSpk _channels[6];
MidiChannel_PcSpk *_activeChannel;
MidiChannel_PcSpk *_lastActiveChannel;
uint16 _lastActiveOut;
static const byte _outInstrumentData[1024];
static const byte _outputTable1[];
static const byte _outputTable2[];
static const uint16 _effectEnvStepTable[];
static const uint16 _frequencyTable[];
};
} // End of namespace Scumm
#endif

View file

@ -54,7 +54,8 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
// BYTE 00: Channel #
// BYTE 02: BIT 01(0x01): Part on?(1 = yes)
// BIT 02(0x02): Reverb? (1 = yes) [bug #1088045]
// BYTE 04: Priority adjustment [guessing]
// BYTE 03: Priority adjustment(upper 4 bits)
// BYTE 04: Priority adjustment(lower 4 bits)
// BYTE 05: Volume(upper 4 bits) [guessing]
// BYTE 06: Volume(lower 4 bits) [guessing]
// BYTE 07: Pan(upper 4 bits) [bug #1088045]
@ -73,7 +74,7 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
if (part) {
part->set_onoff(p[2] & 0x01);
part->effectLevel((p[2] & 0x02) ? 127 : 0);
part->set_pri(p[4]);
part->set_pri((p[3] << 4) | p[4]);
part->volume((p[5] & 0x0F) << 4 |(p[6] & 0x0F));
part->set_pan((p[7] & 0x0F) << 4 | (p[8] & 0x0F));
part->_percussion = player->_isMIDI ? ((p[9] & 0x08) > 0) : false;
@ -91,8 +92,16 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
// 0 is a valid program number. MI2 tests show that in such
// cases, a regular program change message always seems to follow
// anyway.
if (player->_isMIDI)
if (player->_isMIDI) {
part->_instrument.program((p[15] & 0x0F) << 4 |(p[16] & 0x0F), player->_isMT32);
} else if (se->_pcSpeaker) {
// FIXME/HACK: This is only needed here, since when we use the following line:
// se->copyGlobalInstrument((p[15] & 0x0F) << 4 |(p[16] & 0x0F), &part->_instrument);
// We would not get any instrument for PC Speaker. Because we don't default to an
// "empty" instrument in case the global instrument specified is not set up.
byte empty[23] = {0};
part->_instrument.pcspk(empty);
}
part->sendAll();
}
}
@ -113,11 +122,10 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
++p; // Skip hardware type
part = player->getPart(a);
if (part) {
if (len == 62) {
if (len == 62 || len == 48) {
player->decode_sysex_bytes(p, buf, len - 2);
part->set_instrument((byte *)buf);
} else {
// SPK tracks have len == 48 here, and are not supported
} else {
part->programChange(254); // Must be invalid, but not 255 (which is reserved)
}
}
@ -127,7 +135,8 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
p += 2; // Skip hardware type and... whatever came right before it
a = *p++;
player->decode_sysex_bytes(p, buf, len - 3);
se->setGlobalAdLibInstrument(a, buf);
if (len == 63 || len == 49)
se->setGlobalInstrument(a, buf);
break;
case 33: // Parameter adjust

View file

@ -27,6 +27,7 @@ MODULE_OBJS := \
imuse/imuse_part.o \
imuse/imuse_player.o \
imuse/instrument.o \
imuse/pcspk.o \
imuse/sysex_samnmax.o \
imuse/sysex_scumm.o \
input.o \

View file

@ -71,6 +71,7 @@
#include "scumm/he/cup_player_he.h"
#include "scumm/util.h"
#include "scumm/verbs.h"
#include "scumm/imuse/pcspk.h"
#include "backends/audiocd/audiocd.h"
@ -1870,14 +1871,16 @@ void ScummEngine::setupMusic(int midi) {
MidiDriver *nativeMidiDriver = 0;
MidiDriver *adlibMidiDriver = 0;
if (_musicType != MDT_ADLIB && _musicType != MDT_TOWNS)
if (_musicType != MDT_ADLIB && _musicType != MDT_TOWNS && _musicType != MDT_PCSPK)
nativeMidiDriver = MidiDriver::createMidi(dev);
if (nativeMidiDriver != NULL && _native_mt32)
nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
bool multi_midi = ConfMan.getBool("multi_midi") && _musicType != MDT_NONE && (midi & MDT_ADLIB);
bool multi_midi = ConfMan.getBool("multi_midi") && _musicType != MDT_NONE && _musicType != MDT_PCSPK && (midi & MDT_ADLIB);
if (_musicType == MDT_ADLIB || _musicType == MDT_TOWNS || multi_midi) {
adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB));
adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
} else if (_musicType == MDT_PCSPK) {
adlibMidiDriver = new PcSpkDriver(_mixer);
}
_imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver);
@ -1906,6 +1909,8 @@ void ScummEngine::setupMusic(int midi) {
_imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1);
_imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1);
}
if (_musicType == MDT_PCSPK)
_imuse->property(IMuse::PROP_PC_SPEAKER, 1);
}
}
}

View file

@ -1137,8 +1137,8 @@ int ScummEngine::readSoundResource(ResId idx) {
break;
case MKTAG('S','P','K',' '):
pri = -1;
// if (_musicType == MDT_PCSPK || _musicType == MDT_PCJR)
// pri = 11;
if (_musicType == MDT_PCSPK || _musicType == MDT_PCJR)
pri = 11;
break;
}