From ed993e6350bd4926b4b9c94a014aa0f96a628ce7 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Fri, 8 Jul 2011 04:43:45 +0200 Subject: SCUMM: Initial PC Speaker output implementation for SCUMM v5. This is *not* complete yet. --- engines/scumm/detection_tables.h | 8 +- engines/scumm/imuse/imuse.cpp | 6 + engines/scumm/imuse/imuse_internal.h | 2 + engines/scumm/imuse/imuse_part.cpp | 6 + engines/scumm/imuse/instrument.cpp | 52 +++++ engines/scumm/imuse/instrument.h | 4 +- engines/scumm/imuse/pcspk.cpp | 408 +++++++++++++++++++++++++++++++++++ engines/scumm/imuse/pcspk.h | 147 +++++++++++++ engines/scumm/imuse/sysex_scumm.cpp | 21 +- engines/scumm/module.mk | 1 + engines/scumm/scumm.cpp | 6 +- engines/scumm/sound.cpp | 4 +- 12 files changed, 653 insertions(+), 12 deletions(-) create mode 100644 engines/scumm/imuse/pcspk.cpp create mode 100644 engines/scumm/imuse/pcspk.h (limited to 'engines/scumm') diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index e510c46cf2..11901f7565 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -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}, diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp index 317ef36cb9..961d0690d0 100644 --- a/engines/scumm/imuse/imuse.cpp +++ b/engines/scumm/imuse/imuse.cpp @@ -1674,6 +1674,12 @@ void IMuseInternal::setGlobalAdLibInstrument(byte slot, byte *data) { } } +void IMuseInternal::setGlobalPcSpkInstrument(byte slot, byte *data) { + if (slot < 32) { + _global_adlib_instruments[slot].pcspk(data); + } +} + void IMuseInternal::copyGlobalAdLibInstrument(byte slot, Instrument *dest) { if (slot >= 32) return; diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h index 6a7b9fc7d9..76a8c76683 100644 --- a/engines/scumm/imuse/imuse_internal.h +++ b/engines/scumm/imuse/imuse_internal.h @@ -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); @@ -499,6 +500,7 @@ protected: void reallocateMidiChannels(MidiDriver *midi); void setGlobalAdLibInstrument(byte slot, byte *data); + void setGlobalPcSpkInstrument(byte slot, byte *data); void copyGlobalAdLibInstrument(byte slot, Instrument *dest); bool isNativeMT32() { return _native_mt32; } diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp index 5df8407a96..82ad9b70d2 100644 --- a/engines/scumm/imuse/imuse_part.cpp +++ b/engines/scumm/imuse/imuse_part.cpp @@ -199,6 +199,12 @@ void Part::set_instrument(byte * data) { _instrument.send(_mc); } +void Part::set_instrument_pcspk(byte *data) { + _instrument.pcspk(data); + if (clearToTransmit()) + _instrument.send(_mc); +} + void Part::load_global_instrument(byte slot) { _player->_se->copyGlobalAdLibInstrument(slot, &_instrument); if (clearToTransmit()) diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp index 955700fc2b..581f378655 100644 --- a/engines/scumm/imuse/instrument.cpp +++ b/engines/scumm/imuse/instrument.cpp @@ -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 diff --git a/engines/scumm/imuse/instrument.h b/engines/scumm/imuse/instrument.h index 79cbd49032..34f955518e 100644 --- a/engines/scumm/imuse/instrument.h +++ b/engines/scumm/imuse/instrument.h @@ -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); } diff --git a/engines/scumm/imuse/pcspk.cpp b/engines/scumm/imuse/pcspk.cpp new file mode 100644 index 0000000000..8c6e2a80c5 --- /dev/null +++ b/engines/scumm/imuse/pcspk.cpp @@ -0,0 +1,408 @@ +/* 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/debug.h" +#include "common/textconsole.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; + + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_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); + debug("send"); + _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) { + // TODO + } + } 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(); + } +} + +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(); + } 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; + _pcSpk.play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / frequency, -1); +} + +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]; + //_out.instrument = _owner->_outInstrumentData + note; + _out.unkA = 0; + _out.unkB = _instrument[1]; + _out.unkC = _instrument[2]; + _out.unkE = 0; + _out.unk60 = 0; + _out.active = 1; + + _owner->updateNote(); + + if ((_instrument[5] & 0x80) != 0) { + warning("Effect envelope 1 used"); + } + + if ((_instrument[14] & 0x80) != 0) { + warning("Effect envelope 2 used"); + } +} + +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.enabled) + _out.effectEnvelopeA.modWheelState = (value >> 2); + if (_out.effectEnvelopeB.state && _out.effectDefB.enabled) + _out.effectEnvelopeB.modWheelState = (value >> 2); + break; + + case 7: + _tl = value; + // TODO: Properly implement this + _owner->updateNote(); + 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)); +} + +/*const byte PcSpkDriver::_outInstrumentData[] = { + 0 +};*/ + +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::_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 + diff --git a/engines/scumm/imuse/pcspk.h b/engines/scumm/imuse/pcspk.h new file mode 100644 index 0000000000..8edf86e927 --- /dev/null +++ b/engines/scumm/imuse/pcspk.h @@ -0,0 +1,147 @@ +/* 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; + + void updateNote(); + void output(uint16 out); + + struct EffectEnvelope { + uint8 state; + uint16 currentLevel; + uint16 duration; + uint16 maxLevel; + uint16 startLevel; + uint8 loop; + uint8 stateTargetLevels[4]; + uint8 stateModWheelLevels[4]; + uint8 modWheelSensitivity; + uint8 modWheelState; + uint8 modWheelLast; + uint16 stateNumSteps; + uint16 stateStepCounter; + uint16 changePerStep; + uint16 dir; + uint16 changePerStepRem; + uint16 changeCountRem; + }; + + struct EffectDefinition { + uint16 phase; + uint8 type; + uint8 enabled; + EffectEnvelope *envelope; + }; + + struct MidiChannel_PcSpk; + + struct OutputChannel { + uint8 active; + uint8 note; + uint8 sustainNoteOff; + uint8 length; + //const uint8 *instrument; + uint8 unkA; + uint8 unkB; + uint8 unkC; + uint16 unkE; + EffectEnvelope effectEnvelopeA; + EffectDefinition effectDefA; + EffectEnvelope effectEnvelopeB; + EffectDefinition effectDefB; + uint16 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; + }; + + MidiChannel_PcSpk _channels[6]; + MidiChannel_PcSpk *_activeChannel; + + //static const byte _outInstrumentData[]; + static const byte _outputTable1[]; + static const byte _outputTable2[]; + static const uint16 _frequencyTable[]; +}; + +} // End of namespace Scumm + +#endif + diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp index c3bec93a60..160cdf500a 100644 --- a/engines/scumm/imuse/sysex_scumm.cpp +++ b/engines/scumm/imuse/sysex_scumm.cpp @@ -91,8 +91,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 { + // FIXME/HACK: This is only needed here, since when we use the following line: + // se->copyGlobalAdLibInstrument((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(); } } @@ -116,8 +124,10 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) { if (len == 62) { 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 if (len == 48) { + player->decode_sysex_bytes(p, buf, len - 2); + part->set_instrument_pcspk((byte *)buf); + } else { part->programChange(254); // Must be invalid, but not 255 (which is reserved) } } @@ -127,7 +137,10 @@ 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) + se->setGlobalAdLibInstrument(a, buf); + else if (len == 49) + se->setGlobalPcSpkInstrument(a, buf); break; case 33: // Parameter adjust diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 1a60564a9e..99ffdf7f21 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -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 \ diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 7b136dc36d..ed1c90145d 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -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" @@ -1857,7 +1858,7 @@ 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); @@ -1865,6 +1866,9 @@ void ScummEngine::setupMusic(int midi) { 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) { + // HACK + adlibMidiDriver = new PcSpkDriver(_mixer); } _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver); diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index 27e43b3740..544abe6b1d 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -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; } -- cgit v1.2.3 From bf4091931d8069c757d98003f9363bbe85358606 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 04:54:07 +0200 Subject: SCUMM: Implement effect envelope for PC Speaker output in SCUMM v5. --- engines/scumm/imuse/pcspk.cpp | 423 ++++++++++++++++++++++++++++++++++++++++-- engines/scumm/imuse/pcspk.h | 39 ++-- 2 files changed, 433 insertions(+), 29 deletions(-) (limited to 'engines/scumm') diff --git a/engines/scumm/imuse/pcspk.cpp b/engines/scumm/imuse/pcspk.cpp index 8c6e2a80c5..ceecc3515e 100644 --- a/engines/scumm/imuse/pcspk.cpp +++ b/engines/scumm/imuse/pcspk.cpp @@ -23,6 +23,7 @@ #include "common/debug.h" #include "common/textconsole.h" +#include "common/util.h" namespace Scumm { @@ -43,6 +44,8 @@ int PcSpkDriver::open() { for (uint i = 0; i < 6; ++i) _channels[i].init(this, i); _activeChannel = 0; + _effectTimer = 0; + _randBase = 1; _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); return 0; @@ -58,7 +61,6 @@ void PcSpkDriver::close() { void PcSpkDriver::send(uint32 d) { assert((d & 0x0F) < 6); - debug("send"); _channels[(d & 0x0F)].send(d); } @@ -93,7 +95,19 @@ void PcSpkDriver::onTimer() { if (out.length == 0 || --out.length != 0) { if (out.unkB && out.unkC) { - // TODO + out.unkA += out.unkB; + if (out.instrument) + out.unkE = (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; @@ -103,7 +117,7 @@ void PcSpkDriver::onTimer() { } if (_activeChannel->_tl) { - //output((_activeChannel->_out.note << 7) + _activeChannel->_pitchBend + _activeChannel->_out.unk60 + _activeChannel->_out.unkE); + output((_activeChannel->_out.note << 7) + _activeChannel->_pitchBend + _activeChannel->_out.unk60 + _activeChannel->_out.unkE); } else { _pcSpk.stop(); } @@ -222,7 +236,12 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { _out.note = note; _out.sustainNoteOff = 0; _out.length = _instrument[0]; - //_out.instrument = _owner->_outInstrumentData + note; + + 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]; @@ -232,13 +251,15 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { _owner->updateNote(); - if ((_instrument[5] & 0x80) != 0) { - warning("Effect envelope 1 used"); - } + _out.unkC += PcSpkDriver::getEffectModifier(_instrument[3] + ((velocity & 0xFE) << 4)); + if (_out.unkC > 63) + _out.unkC = 63; - if ((_instrument[14] & 0x80) != 0) { - warning("Effect envelope 2 used"); - } + 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) { @@ -253,9 +274,9 @@ void PcSpkDriver::MidiChannel_PcSpk::pitchBend(int16 bend) { void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) { switch (control) { case 1: - if (_out.effectEnvelopeA.state && _out.effectDefA.enabled) + if (_out.effectEnvelopeA.state && _out.effectDefA.useModWheel) _out.effectEnvelopeA.modWheelState = (value >> 2); - if (_out.effectEnvelopeB.state && _out.effectDefB.enabled) + if (_out.effectEnvelopeB.state && _out.effectDefB.useModWheel) _out.effectEnvelopeB.modWheelState = (value >> 2); break; @@ -295,9 +316,370 @@ void PcSpkDriver::MidiChannel_PcSpk::sysEx_customInstrument(uint32 type, const b memcpy(_instrument, instr, sizeof(_instrument)); } -/*const byte PcSpkDriver::_outInstrumentData[] = { - 0 -};*/ +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[1]; + 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.duration = 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, @@ -353,6 +735,17 @@ const byte PcSpkDriver::_outputTable2[] = { 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, diff --git a/engines/scumm/imuse/pcspk.h b/engines/scumm/imuse/pcspk.h index 8edf86e927..3b63ec8199 100644 --- a/engines/scumm/imuse/pcspk.h +++ b/engines/scumm/imuse/pcspk.h @@ -49,45 +49,49 @@ protected: 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; - uint16 currentLevel; + int16 currentLevel; uint16 duration; - uint16 maxLevel; - uint16 startLevel; + int16 maxLevel; + int16 startLevel; uint8 loop; uint8 stateTargetLevels[4]; uint8 stateModWheelLevels[4]; uint8 modWheelSensitivity; uint8 modWheelState; uint8 modWheelLast; - uint16 stateNumSteps; - uint16 stateStepCounter; - uint16 changePerStep; - uint16 dir; - uint16 changePerStepRem; - uint16 changeCountRem; + int16 stateNumSteps; + int16 stateStepCounter; + int16 changePerStep; + int8 dir; + int16 changePerStepRem; + int16 changeCountRem; }; struct EffectDefinition { uint16 phase; uint8 type; - uint8 enabled; + uint8 useModWheel; EffectEnvelope *envelope; }; - struct MidiChannel_PcSpk; - struct OutputChannel { uint8 active; uint8 note; uint8 sustainNoteOff; uint8 length; - //const uint8 *instrument; + const uint8 *instrument; uint8 unkA; uint8 unkB; uint8 unkC; @@ -132,12 +136,19 @@ private: 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; - //static const byte _outInstrumentData[]; + static const byte _outInstrumentData[1024]; static const byte _outputTable1[]; static const byte _outputTable2[]; + static const uint16 _effectEnvStepTable[]; static const uint16 _frequencyTable[]; }; -- cgit v1.2.3 From 0a94fc5b7bed22f846f2d28797107496454b0ecb Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 05:13:03 +0200 Subject: SCUMM: Only try to load MT-32 instruments when we have a MIDI output in iMuse. This fixes the melody in Indy4's intro with the PC Speaker output. --- engines/scumm/imuse/imuse_player.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'engines/scumm') diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp index 61b9cad2cb..07c793a4cb 100644 --- a/engines/scumm/imuse/imuse_player.cpp +++ b/engines/scumm/imuse/imuse_player.cpp @@ -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 -- cgit v1.2.3 From abbd29b16ab8f4c893415c630bb529b0e4c194ab Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 05:21:25 +0200 Subject: SCUMM: Limit iMuse default instrument load to PC Speaker output. Albeit the code is marked as a hack inside the source, the original behaved exaclty the same. If the code is removed the PC Speaker output will miss notes, since unlike the original we only output to parts, which have an instrument set up. --- engines/scumm/imuse/imuse.cpp | 15 ++++++++++----- engines/scumm/imuse/imuse.h | 3 ++- engines/scumm/imuse/imuse_internal.h | 5 +++-- engines/scumm/imuse/imuse_part.cpp | 2 +- engines/scumm/imuse/sysex_scumm.cpp | 4 ++-- engines/scumm/scumm.cpp | 3 ++- 6 files changed, 20 insertions(+), 12 deletions(-) (limited to 'engines/scumm') diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp index 961d0690d0..912ab71420 100644 --- a/engines/scumm/imuse/imuse.cpp +++ b/engines/scumm/imuse/imuse.cpp @@ -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; @@ -1670,20 +1675,20 @@ void IMuseInternal::reallocateMidiChannels(MidiDriver *midi) { void IMuseInternal::setGlobalAdLibInstrument(byte slot, byte *data) { if (slot < 32) { - _global_adlib_instruments[slot].adlib(data); + _global_instruments[slot].adlib(data); } } void IMuseInternal::setGlobalPcSpkInstrument(byte slot, byte *data) { if (slot < 32) { - _global_adlib_instruments[slot].pcspk(data); + _global_instruments[slot].pcspk(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); } diff --git a/engines/scumm/imuse/imuse.h b/engines/scumm/imuse/imuse.h index 8014b13409..73f0bb9a90 100644 --- a/engines/scumm/imuse/imuse.h +++ b/engines/scumm/imuse/imuse.h @@ -55,7 +55,8 @@ public: PROP_GS, PROP_LIMIT_PLAYERS, PROP_RECYCLE_PLAYERS, - PROP_GAME_ID + PROP_GAME_ID, + PROP_PC_SPEAKER }; public: diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h index 76a8c76683..8ac9e77535 100644 --- a/engines/scumm/imuse/imuse_internal.h +++ b/engines/scumm/imuse/imuse_internal.h @@ -434,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]; @@ -501,7 +502,7 @@ protected: void reallocateMidiChannels(MidiDriver *midi); void setGlobalAdLibInstrument(byte slot, byte *data); void setGlobalPcSpkInstrument(byte slot, byte *data); - void copyGlobalAdLibInstrument(byte slot, Instrument *dest); + void copyGlobalInstrument(byte slot, Instrument *dest); bool isNativeMT32() { return _native_mt32; } protected: diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp index 82ad9b70d2..630dd35442 100644 --- a/engines/scumm/imuse/imuse_part.cpp +++ b/engines/scumm/imuse/imuse_part.cpp @@ -206,7 +206,7 @@ void Part::set_instrument_pcspk(byte *data) { } void Part::load_global_instrument(byte slot) { - _player->_se->copyGlobalAdLibInstrument(slot, &_instrument); + _player->_se->copyGlobalInstrument(slot, &_instrument); if (clearToTransmit()) _instrument.send(_mc); } diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp index 160cdf500a..035953f140 100644 --- a/engines/scumm/imuse/sysex_scumm.cpp +++ b/engines/scumm/imuse/sysex_scumm.cpp @@ -93,9 +93,9 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) { // anyway. if (player->_isMIDI) { part->_instrument.program((p[15] & 0x0F) << 4 |(p[16] & 0x0F), player->_isMT32); - } else { + } else if (se->_pcSpeaker) { // FIXME/HACK: This is only needed here, since when we use the following line: - // se->copyGlobalAdLibInstrument((p[15] & 0x0F) << 4 |(p[16] & 0x0F), &part->_instrument); + // 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}; diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index ed1c90145d..22b02e8e03 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -1867,7 +1867,6 @@ void ScummEngine::setupMusic(int 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) { - // HACK adlibMidiDriver = new PcSpkDriver(_mixer); } @@ -1897,6 +1896,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); } } } -- cgit v1.2.3 From fb4ed2224fa744139f800315d1231c8a031d9c75 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 05:28:20 +0200 Subject: SCUMM: Minor clean up in iMuse instrument handling code. --- engines/scumm/imuse/imuse.cpp | 13 +++++-------- engines/scumm/imuse/imuse_internal.h | 3 +-- engines/scumm/imuse/imuse_part.cpp | 10 ++++------ engines/scumm/imuse/sysex_scumm.cpp | 11 +++-------- 4 files changed, 13 insertions(+), 24 deletions(-) (limited to 'engines/scumm') diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp index 912ab71420..9fb6087e53 100644 --- a/engines/scumm/imuse/imuse.cpp +++ b/engines/scumm/imuse/imuse.cpp @@ -1673,15 +1673,12 @@ void IMuseInternal::reallocateMidiChannels(MidiDriver *midi) { } } -void IMuseInternal::setGlobalAdLibInstrument(byte slot, byte *data) { +void IMuseInternal::setGlobalInstrument(byte slot, byte *data) { if (slot < 32) { - _global_instruments[slot].adlib(data); - } -} - -void IMuseInternal::setGlobalPcSpkInstrument(byte slot, byte *data) { - if (slot < 32) { - _global_instruments[slot].pcspk(data); + if (_pcSpeaker) + _global_instruments[slot].pcspk(data); + else + _global_instruments[slot].adlib(data); } } diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h index 8ac9e77535..4ab25907a4 100644 --- a/engines/scumm/imuse/imuse_internal.h +++ b/engines/scumm/imuse/imuse_internal.h @@ -500,8 +500,7 @@ protected: int setImuseMasterVolume(uint vol); void reallocateMidiChannels(MidiDriver *midi); - void setGlobalAdLibInstrument(byte slot, byte *data); - void setGlobalPcSpkInstrument(byte slot, byte *data); + void setGlobalInstrument(byte slot, byte *data); void copyGlobalInstrument(byte slot, Instrument *dest); bool isNativeMT32() { return _native_mt32; } diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp index 630dd35442..53627f6a4a 100644 --- a/engines/scumm/imuse/imuse_part.cpp +++ b/engines/scumm/imuse/imuse_part.cpp @@ -194,13 +194,11 @@ void Part::set_onoff(bool on) { } void Part::set_instrument(byte * data) { - _instrument.adlib(data); - if (clearToTransmit()) - _instrument.send(_mc); -} + if (_se->_pcSpeaker) + _instrument.pcspk(data); + else + _instrument.adlib(data); -void Part::set_instrument_pcspk(byte *data) { - _instrument.pcspk(data); if (clearToTransmit()) _instrument.send(_mc); } diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp index 035953f140..0987ab5553 100644 --- a/engines/scumm/imuse/sysex_scumm.cpp +++ b/engines/scumm/imuse/sysex_scumm.cpp @@ -121,12 +121,9 @@ 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 if (len == 48) { - player->decode_sysex_bytes(p, buf, len - 2); - part->set_instrument_pcspk((byte *)buf); } else { part->programChange(254); // Must be invalid, but not 255 (which is reserved) } @@ -137,10 +134,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); - if (len == 63) - se->setGlobalAdLibInstrument(a, buf); - else if (len == 49) - se->setGlobalPcSpkInstrument(a, buf); + if (len == 63 || len == 49) + se->setGlobalInstrument(a, buf); break; case 33: // Parameter adjust -- cgit v1.2.3 From dab9c05783c56f6c0b0b3faf65921992bc953954 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 16:21:30 +0200 Subject: SCUMM: Do not allow multi midi mode for PC Speaker output. Thanks to eriktorbjorn for catching this. --- engines/scumm/scumm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'engines/scumm') diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 22b02e8e03..5268231cff 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -1862,7 +1862,7 @@ void ScummEngine::setupMusic(int midi) { 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); -- cgit v1.2.3 From 781b7215c43ffc54ce571fb5ba830aa9dff3fcf9 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 17:26:46 +0200 Subject: SCUMM: Fix PC Speaker sound. Now we only output a new frequency if a new channel was selected or a new output frequency has to be send. This makes the sound much more like in DOSBox. This is not present in the original, but since our timings are different this should make up for that. --- engines/scumm/imuse/pcspk.cpp | 27 ++++++++++++++++++++++++++- engines/scumm/imuse/pcspk.h | 3 +++ 2 files changed, 29 insertions(+), 1 deletion(-) (limited to 'engines/scumm') diff --git a/engines/scumm/imuse/pcspk.cpp b/engines/scumm/imuse/pcspk.cpp index ceecc3515e..6eaf7ad168 100644 --- a/engines/scumm/imuse/pcspk.cpp +++ b/engines/scumm/imuse/pcspk.cpp @@ -47,6 +47,12 @@ int PcSpkDriver::open() { _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; + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); return 0; } @@ -120,6 +126,8 @@ void PcSpkDriver::onTimer() { output((_activeChannel->_out.note << 7) + _activeChannel->_pitchBend + _activeChannel->_out.unk60 + _activeChannel->_out.unkE); } else { _pcSpk.stop(); + _lastActiveChannel = 0; + _lastActiveOut = 0; } } @@ -135,6 +143,8 @@ void PcSpkDriver::updateNote() { if (_activeChannel == 0 || _activeChannel->_tl == 0) { _pcSpk.stop(); + _lastActiveChannel = 0; + _lastActiveOut = 0; } else { output(_activeChannel->_pitchBend + (_activeChannel->_out.note << 7)); } @@ -147,7 +157,15 @@ void PcSpkDriver::output(uint16 out) { byte shift = _outputTable1[v1]; uint16 indexBase = _outputTable2[v1] << 5; uint16 frequency = _frequencyTable[(indexBase + v2) / 2] >> shift; - _pcSpk.play(Audio::PCSpeaker::kWaveFormSquare, 1193180 / frequency, -1); + + // 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) { @@ -249,6 +267,13 @@ void PcSpkDriver::MidiChannel_PcSpk::noteOn(byte note, byte velocity) { _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)); diff --git a/engines/scumm/imuse/pcspk.h b/engines/scumm/imuse/pcspk.h index 3b63ec8199..3f70160988 100644 --- a/engines/scumm/imuse/pcspk.h +++ b/engines/scumm/imuse/pcspk.h @@ -145,6 +145,9 @@ private: 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[]; -- cgit v1.2.3 From 5edb6f9e4a331049e49c04441aa36b67053f605c Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 17:58:41 +0200 Subject: SCUMM: Fix priority settings in iMuse allocate part sysEx command. Formerly we ever only used the lower 4 bit of the priority setting for a new part. The original used a full 8 bit setting though (based on the Indy4 PC Speaker output driver). This fixes missing notes in the Indy4 intro with PC Speaker output. This might affect other outputs too! And could cause regressions in case other outputs implemented priority settings differently. --- engines/scumm/imuse/sysex_scumm.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'engines/scumm') diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp index 0987ab5553..98b8ed934e 100644 --- a/engines/scumm/imuse/sysex_scumm.cpp +++ b/engines/scumm/imuse/sysex_scumm.cpp @@ -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; -- cgit v1.2.3 From adb2bbbb5902f0cb9f9c6ca6db81d57b723e1a23 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 18:06:33 +0200 Subject: SCUMM: Properly implement volume controler for PC Speaker output. --- engines/scumm/imuse/pcspk.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'engines/scumm') diff --git a/engines/scumm/imuse/pcspk.cpp b/engines/scumm/imuse/pcspk.cpp index 6eaf7ad168..1b8afcabe5 100644 --- a/engines/scumm/imuse/pcspk.cpp +++ b/engines/scumm/imuse/pcspk.cpp @@ -307,8 +307,15 @@ void PcSpkDriver::MidiChannel_PcSpk::controlChange(byte control, byte value) { case 7: _tl = value; - // TODO: Properly implement this - _owner->updateNote(); + 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: -- cgit v1.2.3 From 5f5daadedbd3dafa80836d6c9a2c865ce0a22f8b Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 18:14:42 +0200 Subject: SCUMM: Let PC Speaker output be controlable via music volume. --- engines/scumm/imuse/pcspk.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'engines/scumm') diff --git a/engines/scumm/imuse/pcspk.cpp b/engines/scumm/imuse/pcspk.cpp index 1b8afcabe5..51f6950bf5 100644 --- a/engines/scumm/imuse/pcspk.cpp +++ b/engines/scumm/imuse/pcspk.cpp @@ -53,7 +53,11 @@ int PcSpkDriver::open() { _lastActiveChannel = 0; _lastActiveOut = 0; - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); + // 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; } -- cgit v1.2.3 From 4ee228bb1692c4f77cea861cdea05282f4567591 Mon Sep 17 00:00:00 2001 From: Johannes Schickel Date: Sun, 10 Jul 2011 19:23:59 +0200 Subject: SCUMM: Fix some envelope bugs in PC Speaker output. --- engines/scumm/imuse/pcspk.cpp | 10 ++++------ engines/scumm/imuse/pcspk.h | 8 ++++---- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'engines/scumm') diff --git a/engines/scumm/imuse/pcspk.cpp b/engines/scumm/imuse/pcspk.cpp index 51f6950bf5..01e2ab3b7d 100644 --- a/engines/scumm/imuse/pcspk.cpp +++ b/engines/scumm/imuse/pcspk.cpp @@ -21,8 +21,6 @@ #include "scumm/imuse/pcspk.h" -#include "common/debug.h" -#include "common/textconsole.h" #include "common/util.h" namespace Scumm { @@ -107,7 +105,7 @@ void PcSpkDriver::onTimer() { if (out.unkB && out.unkC) { out.unkA += out.unkB; if (out.instrument) - out.unkE = (out.instrument[out.unkA] * out.unkC) >> 4; + out.unkE = ((int8)out.instrument[out.unkA] * out.unkC) >> 4; } ++_effectTimer; @@ -515,7 +513,7 @@ void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope break; case 3: - chan._out.unkC = (def.phase & 0xFF) + chan._instrument[1]; + chan._out.unkC = (def.phase & 0xFF) + chan._instrument[2]; break; case 4: @@ -540,10 +538,10 @@ void PcSpkDriver::updateEffectGenerator(MidiChannel_PcSpk &chan, EffectEnvelope } uint8 PcSpkDriver::advanceEffectEnvelope(EffectEnvelope &env, EffectDefinition &def) { - if (env.duration > 0) { + if (env.duration != 0) { env.duration -= 17; if (env.duration <= 0) { - env.duration = 0; + env.state = 0; return 0; } } diff --git a/engines/scumm/imuse/pcspk.h b/engines/scumm/imuse/pcspk.h index 3f70160988..e77ac8c1bf 100644 --- a/engines/scumm/imuse/pcspk.h +++ b/engines/scumm/imuse/pcspk.h @@ -62,7 +62,7 @@ private: struct EffectEnvelope { uint8 state; int16 currentLevel; - uint16 duration; + int16 duration; int16 maxLevel; int16 startLevel; uint8 loop; @@ -80,7 +80,7 @@ private: }; struct EffectDefinition { - uint16 phase; + int16 phase; uint8 type; uint8 useModWheel; EffectEnvelope *envelope; @@ -95,12 +95,12 @@ private: uint8 unkA; uint8 unkB; uint8 unkC; - uint16 unkE; + int16 unkE; EffectEnvelope effectEnvelopeA; EffectDefinition effectDefA; EffectEnvelope effectEnvelopeB; EffectDefinition effectDefB; - uint16 unk60; + int16 unk60; }; struct MidiChannel_PcSpk : public MidiChannel { -- cgit v1.2.3