From 208dbf1d9ad1bcb6e365d5ce9092de962d1d5cd9 Mon Sep 17 00:00:00 2001 From: Walter van Niftrik Date: Thu, 21 Jan 2010 16:27:29 +0000 Subject: SCI: Renamed softseq/ to drivers/ svn-id: r47418 --- engines/sci/console.cpp | 2 +- engines/sci/module.mk | 8 +- engines/sci/sound/drivers/adlib.cpp | 841 ++++++++++++++++++++++++ engines/sci/sound/drivers/amiga.cpp | 676 +++++++++++++++++++ engines/sci/sound/drivers/map-mt32-to-gm.h | 552 ++++++++++++++++ engines/sci/sound/drivers/midi.cpp | 859 +++++++++++++++++++++++++ engines/sci/sound/drivers/mididriver.h | 120 ++++ engines/sci/sound/drivers/pcjr.cpp | 277 ++++++++ engines/sci/sound/iterator/core.cpp | 2 +- engines/sci/sound/iterator/iterator.h | 2 +- engines/sci/sound/iterator/iterator_internal.h | 2 +- engines/sci/sound/midiparser_sci.cpp | 2 +- engines/sci/sound/music.h | 2 +- engines/sci/sound/softseq/adlib.cpp | 841 ------------------------ engines/sci/sound/softseq/amiga.cpp | 676 ------------------- engines/sci/sound/softseq/map-mt32-to-gm.h | 552 ---------------- engines/sci/sound/softseq/midi.cpp | 859 ------------------------- engines/sci/sound/softseq/mididriver.h | 120 ---- engines/sci/sound/softseq/pcjr.cpp | 277 -------- 19 files changed, 3335 insertions(+), 3335 deletions(-) create mode 100644 engines/sci/sound/drivers/adlib.cpp create mode 100644 engines/sci/sound/drivers/amiga.cpp create mode 100644 engines/sci/sound/drivers/map-mt32-to-gm.h create mode 100644 engines/sci/sound/drivers/midi.cpp create mode 100644 engines/sci/sound/drivers/mididriver.h create mode 100644 engines/sci/sound/drivers/pcjr.cpp delete mode 100644 engines/sci/sound/softseq/adlib.cpp delete mode 100644 engines/sci/sound/softseq/amiga.cpp delete mode 100644 engines/sci/sound/softseq/map-mt32-to-gm.h delete mode 100644 engines/sci/sound/softseq/midi.cpp delete mode 100644 engines/sci/sound/softseq/mididriver.h delete mode 100644 engines/sci/sound/softseq/pcjr.cpp diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index 325bf04ef1..e238f9e5b2 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -41,7 +41,7 @@ #else #include "sci/sound/music.h" #endif -#include "sci/sound/softseq/mididriver.h" +#include "sci/sound/drivers/mididriver.h" #include "sci/vocabulary.h" #include "sci/graphics/gui.h" #include "sci/graphics/cursor.h" diff --git a/engines/sci/module.mk b/engines/sci/module.mk index 105cdabb33..10dc73818d 100644 --- a/engines/sci/module.mk +++ b/engines/sci/module.mk @@ -58,10 +58,10 @@ MODULE_OBJS := \ sound/iterator/core.o \ sound/iterator/iterator.o \ sound/iterator/songlib.o \ - sound/softseq/adlib.o \ - sound/softseq/amiga.o \ - sound/softseq/pcjr.o \ - sound/softseq/midi.o \ + sound/drivers/adlib.o \ + sound/drivers/amiga.o \ + sound/drivers/pcjr.o \ + sound/drivers/midi.o \ video/seq_decoder.o \ video/vmd_decoder.o diff --git a/engines/sci/sound/drivers/adlib.cpp b/engines/sci/sound/drivers/adlib.cpp new file mode 100644 index 0000000000..76e615dce0 --- /dev/null +++ b/engines/sci/sound/drivers/adlib.cpp @@ -0,0 +1,841 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "sci/sci.h" + +#include "sound/fmopl.h" +#include "sound/softsynth/emumidi.h" + +#include "sci/resource.h" +#include "sci/sound/drivers/mididriver.h" + +namespace Sci { + +#ifdef __DC__ +#define STEREO false +#else +#define STEREO true +#endif + +// FIXME: We don't seem to be sending the polyphony init data, so disable this for now +#define ADLIB_DISABLE_VOICE_MAPPING + +class MidiDriver_AdLib : public MidiDriver_Emulated { +public: + enum { + kVoices = 9, + kRhythmKeys = 62 + }; + + MidiDriver_AdLib(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0) { } + virtual ~MidiDriver_AdLib() { } + + // MidiDriver + int open(bool isSCI0); + void close(); + void send(uint32 b); + MidiChannel *allocateChannel() { return NULL; } + MidiChannel *getPercussionChannel() { return NULL; } + + // AudioStream + bool isStereo() const { return _stereo; } + int getRate() const { return _mixer->getOutputRate(); } + + // MidiDriver_Emulated + void generateSamples(int16 *buf, int len); + + void setVolume(byte volume); + void playSwitch(bool play); + bool loadResource(const byte *data, uint size); + virtual uint32 property(int prop, uint32 param); + +private: + enum ChannelID { + kLeftChannel = 1, + kRightChannel = 2 + }; + + struct AdLibOperator { + bool amplitudeMod; + bool vibrato; + bool envelopeType; + bool kbScaleRate; + byte frequencyMult; // (0-15) + byte kbScaleLevel; // (0-3) + byte totalLevel; // (0-63, 0=max, 63=min) + byte attackRate; // (0-15) + byte decayRate; // (0-15) + byte sustainLevel; // (0-15) + byte releaseRate; // (0-15) + byte waveForm; // (0-3) + }; + + struct AdLibModulator { + byte feedback; // (0-7) + bool algorithm; + }; + + struct AdLibPatch { + AdLibOperator op[2]; + AdLibModulator mod; + }; + + struct Channel { + uint8 patch; // Patch setting + uint8 volume; // Channel volume (0-63) + uint8 pan; // Pan setting (0-127, 64 is center) + uint8 holdPedal; // Hold pedal setting (0 to 63 is off, 127 to 64 is on) + uint8 extraVoices; // The number of additional voices this channel optimally needs + uint16 pitchWheel; // Pitch wheel setting (0-16383, 8192 is center) + uint8 lastVoice; // Last voice used for this MIDI channel + bool enableVelocity; // Enable velocity control (SCI0) + + Channel() : patch(0), volume(63), pan(64), holdPedal(0), extraVoices(0), + pitchWheel(8192), lastVoice(0), enableVelocity(false) { } + }; + + struct AdLibVoice { + int8 channel; // MIDI channel that this voice is assigned to or -1 + int8 note; // Currently playing MIDI note or -1 + int patch; // Currently playing patch or -1 + uint8 velocity; // Note velocity + bool isSustained; // Flag indicating a note that is being sustained by the hold pedal + uint16 age; // Age of the current note + + AdLibVoice() : channel(-1), note(-1), patch(-1), velocity(0), isSustained(false), age(0) { } + }; + + bool _stereo; + bool _isSCI0; + OPL::OPL *_opl; + bool _playSwitch; + int _masterVolume; + Channel _channels[MIDI_CHANNELS]; + AdLibVoice _voices[kVoices]; + byte *_rhythmKeyMap; + Common::Array _patches; + + void loadInstrument(const byte *ins); + void voiceOn(int voice, int note, int velocity); + void voiceOff(int voice); + void setPatch(int voice, int patch); + void setNote(int voice, int note, bool key); + void setVelocity(int voice); + void setOperator(int oper, AdLibOperator &op); + void setRegister(int reg, int value, int channels = kLeftChannel | kRightChannel); + void renewNotes(int channel, bool key); + void noteOn(int channel, int note, int velocity); + void noteOff(int channel, int note); + int findVoice(int channel); + void voiceMapping(int channel, int voices); + void assignVoices(int channel, int voices); + void releaseVoices(int channel, int voices); + void donateVoices(); + int findVoiceBasic(int channel); + void setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan); + int calcVelocity(int voice, int op); +}; + +class MidiPlayer_AdLib : public MidiPlayer { +public: + MidiPlayer_AdLib() { _driver = new MidiDriver_AdLib(g_system->getMixer()); } + ~MidiPlayer_AdLib() { + delete _driver; + _driver = 0; + } + + int open(ResourceManager *resMan); + void close(); + + byte getPlayId(SciVersion soundVersion); + int getPolyphony() const { return MidiDriver_AdLib::kVoices; } + bool hasRhythmChannel() const { return false; } + void setVolume(byte volume) { static_cast(_driver)->setVolume(volume); } + void playSwitch(bool play) { static_cast(_driver)->playSwitch(play); } + void loadInstrument(int idx, byte *data); +}; + +static const byte registerOffset[MidiDriver_AdLib::kVoices] = { + 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12 +}; + +static const byte velocityMap1[64] = { + 0x00, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x12, 0x13, + 0x14, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2d, 0x2d, 0x2e, + 0x2f, 0x30, 0x31, 0x32, 0x32, 0x33, 0x34, 0x34, + 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3a, + 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, + 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f +}; + +static const byte velocityMap2[64] = { + 0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x21, + 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x2f, 0x30, + 0x31, 0x32, 0x32, 0x33, 0x34, 0x34, 0x35, 0x36, + 0x36, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a, 0x3a, + 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, + 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f +}; + +static const int ym3812_note[13] = { + 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, + 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287, + 0x2ae +}; + +int MidiDriver_AdLib::open(bool isSCI0) { + int rate = _mixer->getOutputRate(); + + _stereo = STEREO; + + debug(3, "ADLIB: Starting driver in %s mode", (isSCI0 ? "SCI0" : "SCI1")); + _isSCI0 = isSCI0; + + _opl = OPL::Config::create(isStereo() ? OPL::Config::kDualOpl2 : OPL::Config::kOpl2); + + // Try falling back to mono, thus plain OPL2 emualtor, when no Dual OPL2 is available. + if (!_opl && _stereo) { + _stereo = false; + _opl = OPL::Config::create(OPL::Config::kOpl2); + } + + if (!_opl) + return -1; + + _opl->init(rate); + + setRegister(0xBD, 0); + setRegister(0x08, 0); + setRegister(0x01, 0x20); + + MidiDriver_Emulated::open(); + + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); + + return 0; +} + +void MidiDriver_AdLib::close() { + _mixer->stopHandle(_mixerSoundHandle); + + delete _opl; + delete[] _rhythmKeyMap; +} + +void MidiDriver_AdLib::setVolume(byte volume) { + _masterVolume = volume; + renewNotes(-1, true); +} + +// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php +void MidiDriver_AdLib::send(uint32 b) { + byte command = b & 0xf0; + byte channel = b & 0xf; + byte op1 = (b >> 8) & 0xff; + byte op2 = (b >> 16) & 0xff; + + switch (command) { + case 0x80: + noteOff(channel, op1); + break; + case 0x90: + noteOn(channel, op1, op2); + break; + case 0xe0: + _channels[channel].pitchWheel = (op1 & 0x7f) | ((op2 & 0x7f) << 7); + renewNotes(channel, true); + break; + case 0xb0: + switch (op1) { + case 0x07: + _channels[channel].volume = op2 >> 1; + renewNotes(channel, true); + break; + case 0x0a: + _channels[channel].pan = op2; + renewNotes(channel, true); + break; + case 0x40: + _channels[channel].holdPedal = op2; + if (op2 == 0) { + for (int i = 0; i < kVoices; i++) { + if ((_voices[i].channel == channel) && _voices[i].isSustained) + voiceOff(i); + } + } + break; + case 0x4b: +#ifndef ADLIB_DISABLE_VOICE_MAPPING + voiceMapping(channel, op2); +#endif + break; + case 0x4e: + _channels[channel].enableVelocity = op2; + break; + case SCI_MIDI_CHANNEL_NOTES_OFF: + for (int i = 0; i < kVoices; i++) + if ((_voices[i].channel == channel) && (_voices[i].note != -1)) + voiceOff(i); + break; + default: + //warning("ADLIB: ignoring MIDI command %02x %02x %02x", command | channel, op1, op2); + break; + } + break; + case 0xc0: + _channels[channel].patch = op1; + break; + // The original adlib driver from sierra ignores aftertouch completely, so should we + case 0xa0: // Polyphonic key pressure (aftertouch) + case 0xd0: // Channel pressure (aftertouch) + break; + case 0xf0: // SysEx, ignore it + break; + default: + warning("ADLIB: Unknown event %02x", command); + } +} + +void MidiDriver_AdLib::generateSamples(int16 *data, int len) { + if (isStereo()) + len <<= 1; + _opl->readBuffer(data, len); + + // Increase the age of the notes + for (int i = 0; i < kVoices; i++) { + if (_voices[i].note != -1) + _voices[i].age++; + } +} + +void MidiDriver_AdLib::loadInstrument(const byte *ins) { + AdLibPatch patch; + + // Set data for the operators + for (int i = 0; i < 2; i++) { + const byte *op = ins + i * 13; + patch.op[i].kbScaleLevel = op[0] & 0x3; + patch.op[i].frequencyMult = op[1] & 0xf; + patch.op[i].attackRate = op[3] & 0xf; + patch.op[i].sustainLevel = op[4] & 0xf; + patch.op[i].envelopeType = op[5]; + patch.op[i].decayRate = op[6] & 0xf; + patch.op[i].releaseRate = op[7] & 0xf; + patch.op[i].totalLevel = op[8] & 0x3f; + patch.op[i].amplitudeMod = op[9]; + patch.op[i].vibrato = op[10]; + patch.op[i].kbScaleRate = op[11]; + } + patch.op[0].waveForm = ins[26] & 0x3; + patch.op[1].waveForm = ins[27] & 0x3; + + // Set data for the modulator + patch.mod.feedback = ins[2] & 0x7; + patch.mod.algorithm = !ins[12]; // Flag is inverted + + _patches.push_back(patch); +} + +void MidiDriver_AdLib::voiceMapping(int channel, int voices) { + int curVoices = 0; + + for (int i = 0; i < kVoices; i++) + if (_voices[i].channel == channel) + curVoices++; + + curVoices += _channels[channel].extraVoices; + + if (curVoices < voices) { + debug(3, "ADLIB: assigning %i additional voices to channel %i", voices - curVoices, channel); + assignVoices(channel, voices - curVoices); + } else if (curVoices > voices) { + debug(3, "ADLIB: releasing %i voices from channel %i", curVoices - voices, channel); + releaseVoices(channel, curVoices - voices); + donateVoices(); + } +} + +void MidiDriver_AdLib::assignVoices(int channel, int voices) { + assert(voices > 0); + + for (int i = 0; i < kVoices; i++) + if (_voices[i].channel == -1) { + _voices[i].channel = channel; + if (--voices == 0) + return; + } + + _channels[channel].extraVoices += voices; +} + +void MidiDriver_AdLib::releaseVoices(int channel, int voices) { + if (_channels[channel].extraVoices >= voices) { + _channels[channel].extraVoices -= voices; + return; + } + + voices -= _channels[channel].extraVoices; + _channels[channel].extraVoices = 0; + + for (int i = 0; i < kVoices; i++) { + if ((_voices[i].channel == channel) && (_voices[i].note == -1)) { + _voices[i].channel = -1; + if (--voices == 0) + return; + } + } + + for (int i = 0; i < kVoices; i++) { + if (_voices[i].channel == channel) { + voiceOff(i); + _voices[i].channel = -1; + if (--voices == 0) + return; + } + } +} + +void MidiDriver_AdLib::donateVoices() { + int freeVoices = 0; + + for (int i = 0; i < kVoices; i++) + if (_voices[i].channel == -1) + freeVoices++; + + if (freeVoices == 0) + return; + + for (int i = 0; i < MIDI_CHANNELS; i++) { + if (_channels[i].extraVoices >= freeVoices) { + assignVoices(i, freeVoices); + _channels[i].extraVoices -= freeVoices; + return; + } else if (_channels[i].extraVoices > 0) { + assignVoices(i, _channels[i].extraVoices); + freeVoices -= _channels[i].extraVoices; + _channels[i].extraVoices = 0; + } + } +} + +void MidiDriver_AdLib::renewNotes(int channel, bool key) { + for (int i = 0; i < kVoices; i++) { + // Update all notes playing this channel + if ((channel == -1) || (_voices[i].channel == channel)) { + if (_voices[i].note != -1) + setNote(i, _voices[i].note, key); + } + } +} + +void MidiDriver_AdLib::noteOn(int channel, int note, int velocity) { + if (velocity == 0) + return noteOff(channel, note); + + velocity >>= 1; + + // Check for playable notes + if ((note < 12) || (note > 107)) + return; + + for (int i = 0; i < kVoices; i++) { + if ((_voices[i].channel == channel) && (_voices[i].note == note)) { + voiceOff(i); + voiceOn(i, note, velocity); + return; + } + } + +#ifdef ADLIB_DISABLE_VOICE_MAPPING + int voice = findVoiceBasic(channel); +#else + int voice = findVoice(channel); +#endif + + if (voice == -1) { + debug(3, "ADLIB: failed to find free voice assigned to channel %i", channel); + return; + } + + voiceOn(voice, note, velocity); +} + +// FIXME: Temporary, see comment at top of file regarding ADLIB_DISABLE_VOICE_MAPPING +int MidiDriver_AdLib::findVoiceBasic(int channel) { + int voice = -1; + int oldestVoice = -1; + int oldestAge = -1; + + // Try to find a voice assigned to this channel that is free (round-robin) + for (int i = 0; i < kVoices; i++) { + int v = (_channels[channel].lastVoice + i + 1) % kVoices; + + if (_voices[v].note == -1) { + voice = v; + break; + } + + // We also keep track of the oldest note in case the search fails + if (_voices[v].age > oldestAge) { + oldestAge = _voices[v].age; + oldestVoice = v; + } + } + + if (voice == -1) { + if (oldestVoice != -1) { + voiceOff(oldestVoice); + voice = oldestVoice; + } else { + return -1; + } + } + + _voices[voice].channel = channel; + _channels[channel].lastVoice = voice; + return voice; +} + +int MidiDriver_AdLib::findVoice(int channel) { + int voice = -1; + int oldestVoice = -1; + uint32 oldestAge = 0; + + // Try to find a voice assigned to this channel that is free (round-robin) + for (int i = 0; i < kVoices; i++) { + int v = (_channels[channel].lastVoice + i + 1) % kVoices; + + if (_voices[v].channel == channel) { + if (_voices[v].note == -1) { + voice = v; + break; + } + + // We also keep track of the oldest note in case the search fails + // Notes started in the current time slice will not be selected + if (_voices[v].age > oldestAge) { + oldestAge = _voices[v].age; + oldestVoice = v; + } + } + } + + if (voice == -1) { + if (oldestVoice != -1) { + voiceOff(oldestVoice); + voice = oldestVoice; + } else { + return -1; + } + } + + _channels[channel].lastVoice = voice; + return voice; +} + +void MidiDriver_AdLib::noteOff(int channel, int note) { + for (int i = 0; i < kVoices; i++) { + if ((_voices[i].channel == channel) && (_voices[i].note == note)) { + if (_channels[channel].holdPedal) + _voices[i].isSustained = true; + else + voiceOff(i); + return; + } + } +} + +void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) { + int channel = _voices[voice].channel; + int patch; + + _voices[voice].age = 0; + + if ((channel == 9) && _rhythmKeyMap) { + patch = CLIP(note, 27, 88) + 101; + } else { + patch = _channels[channel].patch; + } + + // Set patch if different from current patch + if ((patch != _voices[voice].patch) && _playSwitch) + setPatch(voice, patch); + + _voices[voice].velocity = velocity; + setNote(voice, note, true); +} + +void MidiDriver_AdLib::voiceOff(int voice) { + _voices[voice].isSustained = false; + setNote(voice, _voices[voice].note, 0); + _voices[voice].note = -1; + _voices[voice].age = 0; +} + +void MidiDriver_AdLib::setNote(int voice, int note, bool key) { + int channel = _voices[voice].channel; + int n, fre, oct; + float delta; + int bend = _channels[channel].pitchWheel; + + if ((channel == 9) && _rhythmKeyMap) { + note = _rhythmKeyMap[CLIP(note, 27, 88) - 27]; + } + + _voices[voice].note = note; + + delta = 0; + + n = note % 12; + + if (bend < 8192) + bend = 8192 - bend; + delta = (float)pow(2.0, (bend % 8192) / 8192.0); + + if (bend > 8192) + fre = (int)(ym3812_note[n] * delta); + else + fre = (int)(ym3812_note[n] / delta); + + oct = note / 12 - 1; + + if (oct < 0) + oct = 0; + + if (oct > 7) + oct = 7; + + setRegister(0xA0 + voice, fre & 0xff); + setRegister(0xB0 + voice, (key << 5) | (oct << 2) | (fre >> 8)); + + setVelocity(voice); +} + +void MidiDriver_AdLib::setVelocity(int voice) { + AdLibPatch &patch = _patches[_voices[voice].patch]; + int pan = _channels[_voices[voice].channel].pan; + setVelocityReg(registerOffset[voice] + 3, calcVelocity(voice, 1), patch.op[1].kbScaleLevel, pan); + + // In AM mode we need to set the level for both operators + if (_patches[_voices[voice].patch].mod.algorithm == 1) + setVelocityReg(registerOffset[voice], calcVelocity(voice, 0), patch.op[0].kbScaleLevel, pan); +} + +int MidiDriver_AdLib::calcVelocity(int voice, int op) { + if (_isSCI0) { + int velocity = _masterVolume; + + if (velocity > 0) + velocity += 3; + + if (velocity > 15) + velocity = 15; + + int insVelocity; + if (_channels[_voices[voice].channel].enableVelocity) + insVelocity = _voices[voice].velocity; + else + insVelocity = 63 - _patches[_voices[voice].patch].op[op].totalLevel; + + // Note: Later SCI0 has a static table that is close to this formula, but not exactly the same. + // Early SCI0 does (velocity * (insVelocity / 15)) + return velocity * insVelocity / 15; + } else { + AdLibOperator &oper = _patches[_voices[voice].patch].op[op]; + int velocity = _channels[_voices[voice].channel].volume + 1; + velocity = velocity * (velocityMap1[_voices[voice].velocity] + 1) / 64; + velocity = velocity * (_masterVolume + 1) / 16; + + if (--velocity < 0) + velocity = 0; + + return velocityMap2[velocity] * (63 - oper.totalLevel) / 63; + } +} + +void MidiDriver_AdLib::setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan) { + if (!_playSwitch) + velocity = 0; + + if (isStereo()) { + int velLeft = velocity; + int velRight = velocity; + + if (pan > 0x40) + velLeft = velLeft * (0x7f - pan) / 0x3f; + else if (pan < 0x40) + velRight = velRight * pan / 0x40; + + setRegister(0x40 + regOffset, (kbScaleLevel << 6) | (63 - velLeft), kLeftChannel); + setRegister(0x40 + regOffset, (kbScaleLevel << 6) | (63 - velRight), kRightChannel); + } else { + setRegister(0x40 + regOffset, (kbScaleLevel << 6) | (63 - velocity)); + } +} + +void MidiDriver_AdLib::setPatch(int voice, int patch) { + if ((patch < 0) || ((uint)patch >= _patches.size())) { + warning("ADLIB: Invalid patch %i requested", patch); + patch = 0; + } + + _voices[voice].patch = patch; + AdLibModulator &mod = _patches[patch].mod; + + // Set the common settings for both operators + setOperator(registerOffset[voice], _patches[patch].op[0]); + setOperator(registerOffset[voice] + 3, _patches[patch].op[1]); + + // Set the additional settings for the modulator + byte algorithm = mod.algorithm ? 1 : 0; + setRegister(0xC0 + voice, (mod.feedback << 1) | algorithm); +} + +void MidiDriver_AdLib::setOperator(int reg, AdLibOperator &op) { + setRegister(0x40 + reg, (op.kbScaleLevel << 6) | op.totalLevel); + setRegister(0x60 + reg, (op.attackRate << 4) | op.decayRate); + setRegister(0x80 + reg, (op.sustainLevel << 4) | op.releaseRate); + setRegister(0x20 + reg, (op.amplitudeMod << 7) | (op.vibrato << 6) + | (op.envelopeType << 5) | (op.kbScaleRate << 4) | op.frequencyMult); + setRegister(0xE0 + reg, op.waveForm); +} + +void MidiDriver_AdLib::setRegister(int reg, int value, int channels) { + if (channels & kLeftChannel) { + _opl->write(0x220, reg); + _opl->write(0x221, value); + } + + if (isStereo()) { + if (channels & kRightChannel) { + _opl->write(0x222, reg); + _opl->write(0x223, value); + } + } +} + +void MidiDriver_AdLib::playSwitch(bool play) { + _playSwitch = play; + renewNotes(-1, play); +} + +bool MidiDriver_AdLib::loadResource(const byte *data, uint size) { + if ((size != 1344) && (size != 2690) && (size != 5382)) { + warning("ADLIB: Unsupported patch format (%i bytes)", size); + return false; + } + + for (int i = 0; i < 48; i++) + loadInstrument(data + (28 * i)); + + if (size == 2690) { + for (int i = 48; i < 96; i++) + loadInstrument(data + 2 + (28 * i)); + } else if (size == 5382) { + for (int i = 48; i < 190; i++) + loadInstrument(data + (28 * i)); + _rhythmKeyMap = new byte[kRhythmKeys]; + memcpy(_rhythmKeyMap, data + 5320, kRhythmKeys); + } + + return true; +} + +uint32 MidiDriver_AdLib::property(int prop, uint32 param) { + switch(prop) { + case MIDI_PROP_MASTER_VOLUME: + if (param != 0xffff) + _masterVolume = param; + return _masterVolume; + default: + break; + } + return 0; +} + + +int MidiPlayer_AdLib::open(ResourceManager *resMan) { + assert(resMan != NULL); + + // Load up the patch.003 file, parse out the instruments + Resource *res = resMan->findResource(ResourceId(kResourceTypePatch, 3), 0); + bool ok = false; + + if (res) { + ok = static_cast(_driver)->loadResource(res->data, res->size); + } else { + // Early SCI0 games have the sound bank embedded in the adlib driver + + Common::File f; + + if (f.open("ADL.DRV")) { + int size = f.size(); + const uint patchSize = 1344; + + if ((size == 5684) || (size == 5720) || (size == 5727)) { + byte *buf = new byte[patchSize]; + + if (f.seek(0x45a) && (f.read(buf, patchSize) == patchSize)) + ok = static_cast(_driver)->loadResource(buf, patchSize); + + delete[] buf; + } + } + } + + if (!ok) { + warning("ADLIB: Failed to load patch.003"); + return -1; + } + + return static_cast(_driver)->open(getSciVersion() <= SCI_VERSION_0_LATE); +} + +void MidiPlayer_AdLib::close() { + if (_driver) { + _driver->close(); + } +} + +byte MidiPlayer_AdLib::getPlayId(SciVersion soundVersion) { + switch (soundVersion) { + case SCI_VERSION_0_EARLY: + return 0x01; + case SCI_VERSION_0_LATE: + return 0x04; + default: + return 0x00; + } +} + +MidiPlayer *MidiPlayer_AdLib_create() { + return new MidiPlayer_AdLib(); +} + +} // End of namespace Sci diff --git a/engines/sci/sound/drivers/amiga.cpp b/engines/sci/sound/drivers/amiga.cpp new file mode 100644 index 0000000000..1dca092cc2 --- /dev/null +++ b/engines/sci/sound/drivers/amiga.cpp @@ -0,0 +1,676 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "sound/softsynth/emumidi.h" +#include "sci/sound/drivers/mididriver.h" + +#include "common/file.h" +#include "common/frac.h" +#include "common/util.h" + +namespace Sci { + +/* #define DEBUG */ + +// Frequencies for every note +// FIXME Store only one octave +static const int freq_table[] = { + 58, 62, 65, 69, 73, 78, 82, 87, + 92, 98, 104, 110, 117, 124, 131, 139, + 147, 156, 165, 175, 185, 196, 208, 220, + 234, 248, 262, 278, 294, 312, 331, 350, + 371, 393, 417, 441, 468, 496, 525, 556, + 589, 625, 662, 701, 743, 787, 834, 883, + 936, 992, 1051, 1113, 1179, 1250, 1324, 1403, + 1486, 1574, 1668, 1767, 1872, 1984, 2102, 2227, + 2359, 2500, 2648, 2806, 2973, 3149, 3337, 3535, + 3745, 3968, 4204, 4454, 4719, 5000, 5297, 5612, + 5946, 6299, 6674, 7071, 7491, 7937, 8408, 8908, + 9438, 10000, 10594, 11224, 11892, 12599, 13348, 14142, + 14983, 15874, 16817, 17817, 18877, 20000, 21189, 22449, + 23784, 25198, 26696, 28284, 29966, 31748, 33635, 35635, + 37754, 40000, 42378, 44898, 47568, 50396, 53393, 56568, + 59932, 63496, 67271, 71271, 75509, 80000, 84757, 89796 +}; + +class MidiDriver_Amiga : public MidiDriver_Emulated { +public: + enum { + kVoices = 4 + }; + + MidiDriver_Amiga(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15) { } + virtual ~MidiDriver_Amiga() { } + + // MidiDriver + int open(); + void close(); + void send(uint32 b); + MidiChannel *allocateChannel() { return NULL; } + MidiChannel *getPercussionChannel() { return NULL; } + + // AudioStream + bool isStereo() const { return true; } + int getRate() const { return _mixer->getOutputRate(); } + + // MidiDriver_Emulated + void generateSamples(int16 *buf, int len); + + void setVolume(byte volume); + void playSwitch(bool play); + virtual uint32 property(int prop, uint32 param); + +private: + enum { + kModeLoop = 1 << 0, // Instrument looping flag + kModePitch = 1 << 1 // Instrument pitch changes flag + }; + + enum { + kChannels = 10, + kBaseFreq = 20000, // Samplerate of the instrument bank + kPanLeft = 91, + kPanRight = 164 + }; + + struct Channel { + int instrument; + int volume; + int pan; + }; + + struct Envelope { + int length; // Phase period length in samples + int delta; // Velocity delta per period + int target; // Target velocity + }; + + struct Voice { + int instrument; + int note; + int note_velocity; + int velocity; + int envelope; + int envelope_samples; // Number of samples till next envelope event + int decay; + int looping; + int hw_channel; + frac_t offset; + frac_t rate; + }; + + struct Instrument { + char name[30]; + int mode; + int size; // Size of non-looping part in bytes + int loop_size; // Starting offset and size of loop in bytes + int transpose; // Transpose value in semitones + Envelope envelope[4]; // Envelope + int8 *samples; + int8 *loop; + }; + + struct Bank { + char name[30]; + uint size; + Instrument *instruments[256]; + }; + + bool _playSwitch; + int _masterVolume; + int _frequency; + Envelope _envDecay; + Bank _bank; // Instrument bank + + Channel _channels[MIDI_CHANNELS]; + /* Internal channels */ + Voice _voices[kChannels]; + + void setEnvelope(Voice *channel, Envelope *envelope, int phase); + int interpolate(int8 *samples, frac_t offset); + void playInstrument(int16 *dest, Voice *channel, int count); + void changeInstrument(int channel, int instrument); + void stopChannel(int ch); + void stopNote(int ch, int note); + void startNote(int ch, int note, int velocity); + Instrument *readInstrument(Common::File &file, int *id); +}; + +void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase) { + channel->envelope = phase; + channel->envelope_samples = envelope[phase].length; + + if (phase == 0) + channel->velocity = channel->note_velocity / 2; + else + channel->velocity = envelope[phase - 1].target; +} + +int MidiDriver_Amiga::interpolate(int8 *samples, frac_t offset) { + int x = fracToInt(offset); + int diff = (samples[x + 1] - samples[x]) << 8; + + return (samples[x] << 8) + fracToInt(diff * (offset & FRAC_LO_MASK)); +} + +void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) { + int index = 0; + int vol = _channels[channel->hw_channel].volume; + Instrument *instrument = _bank.instruments[channel->instrument]; + + while (1) { + /* Available source samples until end of segment */ + frac_t lin_avail; + int seg_end, rem, i, amount; + int8 *samples; + + if (channel->looping) { + samples = instrument->loop; + seg_end = instrument->loop_size; + } else { + samples = instrument->samples; + seg_end = instrument->size; + } + + lin_avail = intToFrac(seg_end) - channel->offset; + + rem = count - index; + + /* Amount of destination samples that we will compute this iteration */ + amount = lin_avail / channel->rate; + + if (lin_avail % channel->rate) + amount++; + + if (amount > rem) + amount = rem; + + /* Stop at next envelope event */ + if ((channel->envelope_samples != -1) && (amount > channel->envelope_samples)) + amount = channel->envelope_samples; + + for (i = 0; i < amount; i++) { + dest[index++] = interpolate(samples, channel->offset) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127); + channel->offset += channel->rate; + } + + if (channel->envelope_samples != -1) + channel->envelope_samples -= amount; + + if (channel->envelope_samples == 0) { + Envelope *envelope; + int delta, target, velocity; + + if (channel->decay) + envelope = &_envDecay; + else + envelope = &instrument->envelope[channel->envelope]; + + delta = envelope->delta; + target = envelope->target; + velocity = channel->velocity - envelope->delta; + + /* Check whether we have reached the velocity target for the current phase */ + if ((delta >= 0 && velocity <= target) || (delta < 0 && velocity >= target)) { + channel->velocity = target; + + /* Stop note after velocity has dropped to 0 */ + if (target == 0) { + channel->note = -1; + break; + } else + switch (channel->envelope) { + case 0: + case 2: + /* Go to next phase */ + setEnvelope(channel, instrument->envelope, channel->envelope + 1); + break; + case 1: + case 3: + /* Stop envelope */ + channel->envelope_samples = -1; + break; + } + } else { + /* We haven't reached the target yet */ + channel->envelope_samples = envelope->length; + channel->velocity = velocity; + } + } + + if (index == count) + break; + + if (fracToInt(channel->offset) >= seg_end) { + if (instrument->mode & kModeLoop) { + /* Loop the samples */ + channel->offset -= intToFrac(seg_end); + channel->looping = 1; + } else { + /* All samples have been played */ + channel->note = -1; + break; + } + } + } +} + +void MidiDriver_Amiga::changeInstrument(int channel, int instrument) { +#ifdef DEBUG + if (_bank.instruments[instrument]) + printf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument); + else + warning("[sfx:seq:amiga] instrument %i does not exist (channel %i)", instrument, channel); +#endif + _channels[channel].instrument = instrument; +} + +void MidiDriver_Amiga::stopChannel(int ch) { + int i; + + /* Start decay phase for note on this hw channel, if any */ + for (i = 0; i < kChannels; i++) + if (_voices[i].note != -1 && _voices[i].hw_channel == ch && !_voices[i].decay) { + /* Trigger fast decay envelope */ + _voices[i].decay = 1; + _voices[i].envelope_samples = _envDecay.length; + break; + } +} + +void MidiDriver_Amiga::stopNote(int ch, int note) { + int channel; + Instrument *instrument; + + for (channel = 0; channel < kChannels; channel++) + if (_voices[channel].note == note && _voices[channel].hw_channel == ch && !_voices[channel].decay) + break; + + if (channel == kChannels) { +#ifdef DEBUG + warning("[sfx:seq:amiga] cannot stop note %i on channel %i", note, ch); +#endif + return; + } + + instrument = _bank.instruments[_voices[channel].instrument]; + + /* Start the envelope phases for note-off if looping is on and envelope is enabled */ + if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0)) + setEnvelope(&_voices[channel], instrument->envelope, 2); +} + +void MidiDriver_Amiga::startNote(int ch, int note, int velocity) { + Instrument *instrument; + int channel; + + if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) { + warning("[sfx:seq:amiga] invalid instrument %i on channel %i", _channels[ch].instrument, ch); + return; + } + + instrument = _bank.instruments[_channels[ch].instrument]; + + if (!instrument) { + warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument); + return; + } + + for (channel = 0; channel < kChannels; channel++) + if (_voices[channel].note == -1) + break; + + if (channel == kChannels) { + warning("[sfx:seq:amiga] could not find a free channel"); + return; + } + + stopChannel(ch); + + if (instrument->mode & kModePitch) { + int fnote = note + instrument->transpose; + + if (fnote < 0 || fnote > 127) { + warning("[sfx:seq:amiga] illegal note %i\n", fnote); + return; + } + + /* Compute rate for note */ + _voices[channel].rate = doubleToFrac(freq_table[fnote] / (double) _frequency); + } else + _voices[channel].rate = doubleToFrac(kBaseFreq / (double) _frequency); + + _voices[channel].instrument = _channels[ch].instrument; + _voices[channel].note = note; + _voices[channel].note_velocity = velocity; + + if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0)) + setEnvelope(&_voices[channel], instrument->envelope, 0); + else { + _voices[channel].velocity = 64; + _voices[channel].envelope_samples = -1; + } + + _voices[channel].offset = 0; + _voices[channel].hw_channel = ch; + _voices[channel].decay = 0; + _voices[channel].looping = 0; +} + +MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &file, int *id) { + Instrument *instrument; + byte header[61]; + int size; + int seg_size[3]; + int loop_offset; + int i; + + if (file.read(header, 61) < 61) { + warning("[sfx:seq:amiga] failed to read instrument header"); + return NULL; + } + + instrument = new Instrument; + + seg_size[0] = READ_BE_UINT16(header + 35) * 2; + seg_size[1] = READ_BE_UINT16(header + 41) * 2; + seg_size[2] = READ_BE_UINT16(header + 47) * 2; + + instrument->mode = header[33]; + instrument->transpose = (int8) header[34]; + for (i = 0; i < 4; i++) { + int length = (int8) header[49 + i]; + + if (length == 0 && i > 0) + length = 256; + + instrument->envelope[i].length = length * _frequency / 60; + instrument->envelope[i].delta = (int8)header[53 + i]; + instrument->envelope[i].target = header[57 + i]; + } + /* Final target must be 0 */ + instrument->envelope[3].target = 0; + + loop_offset = READ_BE_UINT32(header + 37) & ~1; + size = seg_size[0] + seg_size[1] + seg_size[2]; + + *id = READ_BE_UINT16(header); + + strncpy(instrument->name, (char *) header + 2, 29); + instrument->name[29] = 0; +#ifdef DEBUG + printf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n", + *id, instrument->name, size); + printf(" Mode: %02x\n", instrument->mode); + printf(" Looping: %s\n", instrument->mode & kModeLoop ? "on" : "off"); + printf(" Pitch changes: %s\n", instrument->mode & kModePitch ? "on" : "off"); + printf(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]); + printf(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43)); +#endif + instrument->samples = (int8 *) malloc(size + 1); + if (file.read(instrument->samples, size) < (unsigned int)size) { + warning("[sfx:seq:amiga] failed to read instrument samples"); + free(instrument->samples); + delete instrument; + return NULL; + } + + if (instrument->mode & kModeLoop) { + if (loop_offset + seg_size[1] > size) { +#ifdef DEBUG + warning("[sfx:seq:amiga] looping samples extend %i bytes past end of sample block", + loop_offset + seg_size[1] - size); +#endif + seg_size[1] = size - loop_offset; + } + + if (seg_size[1] < 0) { + warning("[sfx:seq:amiga] invalid looping point"); + free(instrument->samples); + delete instrument; + return NULL; + } + + instrument->size = seg_size[0]; + instrument->loop_size = seg_size[1]; + + instrument->loop = (int8*)malloc(instrument->loop_size + 1); + memcpy(instrument->loop, instrument->samples + loop_offset, instrument->loop_size); + + instrument->samples[instrument->size] = instrument->loop[0]; + instrument->loop[instrument->loop_size] = instrument->loop[0]; + } else { + instrument->loop = NULL; + instrument->size = size; + instrument->samples[instrument->size] = 0; + } + + return instrument; +} + +uint32 MidiDriver_Amiga::property(int prop, uint32 param) { + switch(prop) { + case MIDI_PROP_MASTER_VOLUME: + if (param != 0xffff) + _masterVolume = param; + return _masterVolume; + default: + break; + } + return 0; +} + +int MidiDriver_Amiga::open() { + _frequency = _mixer->getOutputRate(); + _envDecay.length = _frequency / (32 * 64); + _envDecay.delta = 1; + _envDecay.target = 0; + + Common::File file; + byte header[40]; + + if (!file.open("bank.001")) { + warning("[sfx:seq:amiga] file bank.001 not found"); + return Common::kUnknownError; + } + + if (file.read(header, 40) < 40) { + warning("[sfx:seq:amiga] failed to read header of file bank.001"); + return Common::kUnknownError; + } + + for (uint i = 0; i < 256; i++) + _bank.instruments[i] = NULL; + + for (uint i = 0; i < kChannels; i++) { + _voices[i].note = -1; + _voices[i].hw_channel = 0; + } + + for (uint i = 0; i < MIDI_CHANNELS; i++) { + _channels[i].instrument = -1; + _channels[i].volume = 127; + _channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? kPanLeft : kPanRight); + } + + _bank.size = READ_BE_UINT16(header + 38); + strncpy(_bank.name, (char *) header + 8, 29); + _bank.name[29] = 0; +#ifdef DEBUG + printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name); +#endif + + for (uint i = 0; i < _bank.size; i++) { + int id; + Instrument *instrument = readInstrument(file, &id); + + if (!instrument) { + warning("[sfx:seq:amiga] failed to read bank.001"); + return Common::kUnknownError; + } + + if (id < 0 || id > 255) { + warning("[sfx:seq:amiga] Error: instrument ID out of bounds"); + return Common::kUnknownError; + } + + _bank.instruments[id] = instrument; + } + + MidiDriver_Emulated::open(); + + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); + + return Common::kNoError; +} + +void MidiDriver_Amiga::close() { + _mixer->stopHandle(_mixerSoundHandle); + + for (uint i = 0; i < _bank.size; i++) { + if (_bank.instruments[i]) { + if (_bank.instruments[i]->loop) + free(_bank.instruments[i]->loop); + free(_bank.instruments[i]->samples); + delete _bank.instruments[i]; + } + } +} + +void MidiDriver_Amiga::playSwitch(bool play) { + _playSwitch = play; +} + +void MidiDriver_Amiga::setVolume(byte volume_) { + _masterVolume = volume_; +} + +void MidiDriver_Amiga::send(uint32 b) { + byte command = b & 0xf0; + byte channel = b & 0xf; + byte op1 = (b >> 8) & 0xff; + byte op2 = (b >> 16) & 0xff; + + switch (command) { + case 0x80: + stopNote(channel, op1); + break; + case 0x90: + if (op2 > 0) + startNote(channel, op1, op2); + else + stopNote(channel, op1); + break; + case 0xb0: + switch (op1) { + case 0x07: + _channels[channel].volume = op2; + break; + case 0x0a: +#ifdef DEBUG + warning("[sfx:seq:amiga] ignoring pan 0x%02x event for channel %i", op2, channel); +#endif + break; + case 0x7b: + stopChannel(channel); + break; + default: + warning("[sfx:seq:amiga] unknown control event 0x%02x", op1); + } + break; + case 0xc0: + changeInstrument(channel, op1); + break; + default: + warning("[sfx:seq:amiga] unknown event %02x", command); + } +} + +void MidiDriver_Amiga::generateSamples(int16 *data, int len) { + if (len == 0) + return; + + int16 *buffers = (int16*)malloc(len * 2 * kChannels); + + memset(buffers, 0, len * 2 * kChannels); + + /* Generate samples for all notes */ + for (int i = 0; i < kChannels; i++) + if (_voices[i].note >= 0) + playInstrument(buffers + i * len, &_voices[i], len); + + if (isStereo()) { + for (int j = 0; j < len; j++) { + int mixedl = 0, mixedr = 0; + + /* Mix and pan */ + for (int i = 0; i < kChannels; i++) { + mixedl += buffers[i * len + j] * (256 - _channels[_voices[i].hw_channel].pan); + mixedr += buffers[i * len + j] * _channels[_voices[i].hw_channel].pan; + } + + /* Adjust volume */ + data[2 * j] = mixedl * _masterVolume >> 13; + data[2 * j + 1] = mixedr * _masterVolume >> 13; + } + } else { + for (int j = 0; j < len; j++) { + int mixed = 0; + + /* Mix */ + for (int i = 0; i < kChannels; i++) + mixed += buffers[i * len + j]; + + /* Adjust volume */ + data[j] = mixed * _masterVolume >> 6; + } + } + + free(buffers); +} + +class MidiPlayer_Amiga : public MidiPlayer { +public: + MidiPlayer_Amiga() { _driver = new MidiDriver_Amiga(g_system->getMixer()); } + byte getPlayId(SciVersion soundVersion); + int getPolyphony() const { return MidiDriver_Amiga::kVoices; } + bool hasRhythmChannel() const { return false; } + void setVolume(byte volume) { static_cast(_driver)->setVolume(volume); } + void playSwitch(bool play) { static_cast(_driver)->playSwitch(play); } + void loadInstrument(int idx, byte *data); +}; + +MidiPlayer *MidiPlayer_Amiga_create() { + return new MidiPlayer_Amiga(); +} + +byte MidiPlayer_Amiga::getPlayId(SciVersion soundVersion) { + if (soundVersion != SCI_VERSION_0_LATE) + error("Amiga sound support not available for this SCI version"); + + return 0x40; +} + +} // End of namespace Sci diff --git a/engines/sci/sound/drivers/map-mt32-to-gm.h b/engines/sci/sound/drivers/map-mt32-to-gm.h new file mode 100644 index 0000000000..d87ab777d7 --- /dev/null +++ b/engines/sci/sound/drivers/map-mt32-to-gm.h @@ -0,0 +1,552 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +namespace Sci { + +/* Patch not mapped */ +#define MIDI_UNMAPPED 0xff +/* Patch mapped to rhythm key */ +#define MIDI_MAPPED_TO_RHYTHM 0xfe + +struct Mt32ToGmMap { + const char *name; + uint8 gmInstr; + uint8 gmRhythmKey; +}; + +static const char *GmInstrumentNames[] = { + /*000*/ "Acoustic Grand Piano", + /*001*/ "Bright Acoustic Piano", + /*002*/ "Electric Grand Piano", + /*003*/ "Honky-tonk Piano", + /*004*/ "Electric Piano 1", + /*005*/ "Electric Piano 2", + /*006*/ "Harpsichord", + /*007*/ "Clavinet", + /*008*/ "Celesta", + /*009*/ "Glockenspiel", + /*010*/ "Music Box", + /*011*/ "Vibraphone", + /*012*/ "Marimba", + /*013*/ "Xylophone", + /*014*/ "Tubular Bells", + /*015*/ "Dulcimer", + /*016*/ "Drawbar Organ", + /*017*/ "Percussive Organ", + /*018*/ "Rock Organ", + /*019*/ "Church Organ", + /*020*/ "Reed Organ", + /*021*/ "Accordion", + /*022*/ "Harmonica", + /*023*/ "Tango Accordion", + /*024*/ "Acoustic Guitar (nylon)", + /*025*/ "Acoustic Guitar (steel)", + /*026*/ "Electric Guitar (jazz)", + /*027*/ "Electric Guitar (clean)", + /*028*/ "Electric Guitar (muted)", + /*029*/ "Overdriven Guitar", + /*030*/ "Distortion Guitar", + /*031*/ "Guitar Harmonics", + /*032*/ "Acoustic Bass", + /*033*/ "Electric Bass (finger)", + /*034*/ "Electric Bass (pick)", + /*035*/ "Fretless Bass", + /*036*/ "Slap Bass 1", + /*037*/ "Slap Bass 2", + /*038*/ "Synth Bass 1", + /*039*/ "Synth Bass 2", + /*040*/ "Violin", + /*041*/ "Viola", + /*042*/ "Cello", + /*043*/ "Contrabass", + /*044*/ "Tremolo Strings", + /*045*/ "Pizzicato Strings", + /*046*/ "Orchestral Harp", + /*047*/ "Timpani", + /*048*/ "String Ensemble 1", + /*049*/ "String Ensemble 2", + /*050*/ "SynthStrings 1", + /*051*/ "SynthStrings 2", + /*052*/ "Choir Aahs", + /*053*/ "Voice Oohs", + /*054*/ "Synth Voice", + /*055*/ "Orchestra Hit", + /*056*/ "Trumpet", + /*057*/ "Trombone", + /*058*/ "Tuba", + /*059*/ "Muted Trumpet", + /*060*/ "French Horn", + /*061*/ "Brass Section", + /*062*/ "SynthBrass 1", + /*063*/ "SynthBrass 2", + /*064*/ "Soprano Sax", + /*065*/ "Alto Sax", + /*066*/ "Tenor Sax", + /*067*/ "Baritone Sax", + /*068*/ "Oboe", + /*069*/ "English Horn", + /*070*/ "Bassoon", + /*071*/ "Clarinet", + /*072*/ "Piccolo", + /*073*/ "Flute", + /*074*/ "Recorder", + /*075*/ "Pan Flute", + /*076*/ "Blown Bottle", + /*077*/ "Shakuhachi", + /*078*/ "Whistle", + /*079*/ "Ocarina", + /*080*/ "Lead 1 (square)", + /*081*/ "Lead 2 (sawtooth)", + /*082*/ "Lead 3 (calliope)", + /*083*/ "Lead 4 (chiff)", + /*084*/ "Lead 5 (charang)", + /*085*/ "Lead 6 (voice)", + /*086*/ "Lead 7 (fifths)", + /*087*/ "Lead 8 (bass+lead)", + /*088*/ "Pad 1 (new age)", + /*089*/ "Pad 2 (warm)", + /*090*/ "Pad 3 (polysynth)", + /*091*/ "Pad 4 (choir)", + /*092*/ "Pad 5 (bowed)", + /*093*/ "Pad 6 (metallic)", + /*094*/ "Pad 7 (halo)", + /*095*/ "Pad 8 (sweep)", + /*096*/ "FX 1 (rain)", + /*097*/ "FX 2 (soundtrack)", + /*098*/ "FX 3 (crystal)", + /*099*/ "FX 4 (atmosphere)", + /*100*/ "FX 5 (brightness)", + /*101*/ "FX 6 (goblins)", + /*102*/ "FX 7 (echoes)", + /*103*/ "FX 8 (sci-fi)", + /*104*/ "Sitar", + /*105*/ "Banjo", + /*106*/ "Shamisen", + /*107*/ "Koto", + /*108*/ "Kalimba", + /*109*/ "Bag pipe", + /*110*/ "Fiddle", + /*111*/ "Shannai", + /*112*/ "Tinkle Bell", + /*113*/ "Agogo", + /*114*/ "Steel Drums", + /*115*/ "Woodblock", + /*116*/ "Taiko Drum", + /*117*/ "Melodic Tom", + /*118*/ "Synth Drum", + /*119*/ "Reverse Cymbal", + /*120*/ "Guitar Fret Noise", + /*121*/ "Breath Noise", + /*122*/ "Seashore", + /*123*/ "Bird Tweet", + /*124*/ "Telephone Ring", + /*125*/ "Helicopter", + /*126*/ "Applause", + /*127*/ "Gunshot" +}; + +/* The GM Percussion map is downwards compatible to the MT32 map, which is used in SCI */ +static const char *GmPercussionNames[] = { + /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /*30*/ 0, 0, 0, 0, + /* The preceeding percussions are not covered by the GM standard */ + /*34*/ "Acoustic Bass Drum", + /*35*/ "Bass Drum 1", + /*36*/ "Side Stick", + /*37*/ "Acoustic Snare", + /*38*/ "Hand Clap", + /*39*/ "Electric Snare", + /*40*/ "Low Floor Tom", + /*41*/ "Closed Hi-Hat", + /*42*/ "High Floor Tom", + /*43*/ "Pedal Hi-Hat", + /*44*/ "Low Tom", + /*45*/ "Open Hi-Hat", + /*46*/ "Low-Mid Tom", + /*47*/ "Hi-Mid Tom", + /*48*/ "Crash Cymbal 1", + /*49*/ "High Tom", + /*50*/ "Ride Cymbal 1", + /*51*/ "Chinese Cymbal", + /*52*/ "Ride Bell", + /*53*/ "Tambourine", + /*54*/ "Splash Cymbal", + /*55*/ "Cowbell", + /*56*/ "Crash Cymbal 2", + /*57*/ "Vibraslap", + /*58*/ "Ride Cymbal 2", + /*59*/ "Hi Bongo", + /*60*/ "Low Bongo", + /*61*/ "Mute Hi Conga", + /*62*/ "Open Hi Conga", + /*63*/ "Low Conga", + /*64*/ "High Timbale", + /*65*/ "Low Timbale", + /*66*/ "High Agogo", + /*67*/ "Low Agogo", + /*68*/ "Cabasa", + /*69*/ "Maracas", + /*70*/ "Short Whistle", + /*71*/ "Long Whistle", + /*72*/ "Short Guiro", + /*73*/ "Long Guiro", + /*74*/ "Claves", + /*75*/ "Hi Wood Block", + /*76*/ "Low Wood Block", + /*77*/ "Mute Cuica", + /*78*/ "Open Cuica", + /*79*/ "Mute Triangle", + /*80*/ "Open Triangle" +}; + +/******************************************* + * Fancy instrument mappings begin here... * + *******************************************/ + + +static const Mt32ToGmMap Mt32PresetTimbreMaps[] = { + /*000*/ {"AcouPiano1", 0, MIDI_UNMAPPED}, + /*001*/ {"AcouPiano2", 1, MIDI_UNMAPPED}, + /*002*/ {"AcouPiano3", 0, MIDI_UNMAPPED}, + /*003*/ {"ElecPiano1", 4, MIDI_UNMAPPED}, + /*004*/ {"ElecPiano2", 5, MIDI_UNMAPPED}, + /*005*/ {"ElecPiano3", 4, MIDI_UNMAPPED}, + /*006*/ {"ElecPiano4", 5, MIDI_UNMAPPED}, + /*007*/ {"Honkytonk ", 3, MIDI_UNMAPPED}, + /*008*/ {"Elec Org 1", 16, MIDI_UNMAPPED}, + /*009*/ {"Elec Org 2", 17, MIDI_UNMAPPED}, + /*010*/ {"Elec Org 3", 18, MIDI_UNMAPPED}, + /*011*/ {"Elec Org 4", 18, MIDI_UNMAPPED}, + /*012*/ {"Pipe Org 1", 19, MIDI_UNMAPPED}, + /*013*/ {"Pipe Org 2", 19, MIDI_UNMAPPED}, + /*014*/ {"Pipe Org 3", 20, MIDI_UNMAPPED}, + /*015*/ {"Accordion ", 21, MIDI_UNMAPPED}, + /*016*/ {"Harpsi 1 ", 6, MIDI_UNMAPPED}, + /*017*/ {"Harpsi 2 ", 6, MIDI_UNMAPPED}, + /*018*/ {"Harpsi 3 ", 6, MIDI_UNMAPPED}, + /*019*/ {"Clavi 1 ", 7, MIDI_UNMAPPED}, + /*020*/ {"Clavi 2 ", 7, MIDI_UNMAPPED}, + /*021*/ {"Clavi 3 ", 7, MIDI_UNMAPPED}, + /*022*/ {"Celesta 1 ", 8, MIDI_UNMAPPED}, + /*023*/ {"Celesta 2 ", 8, MIDI_UNMAPPED}, + /*024*/ {"Syn Brass1", 62, MIDI_UNMAPPED}, + /*025*/ {"Syn Brass2", 63, MIDI_UNMAPPED}, + /*026*/ {"Syn Brass3", 62, MIDI_UNMAPPED}, + /*027*/ {"Syn Brass4", 63, MIDI_UNMAPPED}, + /*028*/ {"Syn Bass 1", 38, MIDI_UNMAPPED}, + /*029*/ {"Syn Bass 2", 39, MIDI_UNMAPPED}, + /*030*/ {"Syn Bass 3", 38, MIDI_UNMAPPED}, + /*031*/ {"Syn Bass 4", 39, MIDI_UNMAPPED}, + /*032*/ {"Fantasy ", 88, MIDI_UNMAPPED}, + /*033*/ {"Harmo Pan ", 89, MIDI_UNMAPPED}, + /*034*/ {"Chorale ", 52, MIDI_UNMAPPED}, + /*035*/ {"Glasses ", 98, MIDI_UNMAPPED}, + /*036*/ {"Soundtrack", 97, MIDI_UNMAPPED}, + /*037*/ {"Atmosphere", 99, MIDI_UNMAPPED}, + /*038*/ {"Warm Bell ", 89, MIDI_UNMAPPED}, + /*039*/ {"Funny Vox ", 85, MIDI_UNMAPPED}, + /*040*/ {"Echo Bell ", 39, MIDI_UNMAPPED}, + /*041*/ {"Ice Rain ", 101, MIDI_UNMAPPED}, + /*042*/ {"Oboe 2001 ", 68, MIDI_UNMAPPED}, + /*043*/ {"Echo Pan ", 87, MIDI_UNMAPPED}, + /*044*/ {"DoctorSolo", 86, MIDI_UNMAPPED}, + /*045*/ {"Schooldaze", 103, MIDI_UNMAPPED}, + /*046*/ {"BellSinger", 88, MIDI_UNMAPPED}, + /*047*/ {"SquareWave", 80, MIDI_UNMAPPED}, + /*048*/ {"Str Sect 1", 48, MIDI_UNMAPPED}, + /*049*/ {"Str Sect 2", 48, MIDI_UNMAPPED}, + /*050*/ {"Str Sect 3", 49, MIDI_UNMAPPED}, + /*051*/ {"Pizzicato ", 45, MIDI_UNMAPPED}, + /*052*/ {"Violin 1 ", 40, MIDI_UNMAPPED}, + /*053*/ {"Violin 2 ", 40, MIDI_UNMAPPED}, + /*054*/ {"Cello 1 ", 42, MIDI_UNMAPPED}, + /*055*/ {"Cello 2 ", 42, MIDI_UNMAPPED}, + /*056*/ {"Contrabass", 43, MIDI_UNMAPPED}, + /*057*/ {"Harp 1 ", 46, MIDI_UNMAPPED}, + /*058*/ {"Harp 2 ", 46, MIDI_UNMAPPED}, + /*059*/ {"Guitar 1 ", 24, MIDI_UNMAPPED}, + /*060*/ {"Guitar 2 ", 25, MIDI_UNMAPPED}, + /*061*/ {"Elec Gtr 1", 26, MIDI_UNMAPPED}, + /*062*/ {"Elec Gtr 2", 27, MIDI_UNMAPPED}, + /*063*/ {"Sitar ", 104, MIDI_UNMAPPED}, + /*064*/ {"Acou Bass1", 32, MIDI_UNMAPPED}, + /*065*/ {"Acou Bass2", 33, MIDI_UNMAPPED}, + /*066*/ {"Elec Bass1", 34, MIDI_UNMAPPED}, + /*067*/ {"Elec Bass2", 39, MIDI_UNMAPPED}, + /*068*/ {"Slap Bass1", 36, MIDI_UNMAPPED}, + /*069*/ {"Slap Bass2", 37, MIDI_UNMAPPED}, + /*070*/ {"Fretless 1", 35, MIDI_UNMAPPED}, + /*071*/ {"Fretless 2", 35, MIDI_UNMAPPED}, + /*072*/ {"Flute 1 ", 73, MIDI_UNMAPPED}, + /*073*/ {"Flute 2 ", 73, MIDI_UNMAPPED}, + /*074*/ {"Piccolo 1 ", 72, MIDI_UNMAPPED}, + /*075*/ {"Piccolo 2 ", 72, MIDI_UNMAPPED}, + /*076*/ {"Recorder ", 74, MIDI_UNMAPPED}, + /*077*/ {"Panpipes ", 75, MIDI_UNMAPPED}, + /*078*/ {"Sax 1 ", 64, MIDI_UNMAPPED}, + /*079*/ {"Sax 2 ", 65, MIDI_UNMAPPED}, + /*080*/ {"Sax 3 ", 66, MIDI_UNMAPPED}, + /*081*/ {"Sax 4 ", 67, MIDI_UNMAPPED}, + /*082*/ {"Clarinet 1", 71, MIDI_UNMAPPED}, + /*083*/ {"Clarinet 2", 71, MIDI_UNMAPPED}, + /*084*/ {"Oboe ", 68, MIDI_UNMAPPED}, + /*085*/ {"Engl Horn ", 69, MIDI_UNMAPPED}, + /*086*/ {"Bassoon ", 70, MIDI_UNMAPPED}, + /*087*/ {"Harmonica ", 22, MIDI_UNMAPPED}, + /*088*/ {"Trumpet 1 ", 56, MIDI_UNMAPPED}, + /*089*/ {"Trumpet 2 ", 56, MIDI_UNMAPPED}, + /*090*/ {"Trombone 1", 57, MIDI_UNMAPPED}, + /*091*/ {"Trombone 2", 57, MIDI_UNMAPPED}, + /*092*/ {"Fr Horn 1 ", 60, MIDI_UNMAPPED}, + /*093*/ {"Fr Horn 2 ", 60, MIDI_UNMAPPED}, + /*094*/ {"Tuba ", 58, MIDI_UNMAPPED}, + /*095*/ {"Brs Sect 1", 61, MIDI_UNMAPPED}, + /*096*/ {"Brs Sect 2", 61, MIDI_UNMAPPED}, + /*097*/ {"Vibe 1 ", 11, MIDI_UNMAPPED}, + /*098*/ {"Vibe 2 ", 11, MIDI_UNMAPPED}, + /*099*/ {"Syn Mallet", 15, MIDI_UNMAPPED}, + /*100*/ {"Wind Bell ", 88, MIDI_UNMAPPED}, + /*101*/ {"Glock ", 9, MIDI_UNMAPPED}, + /*102*/ {"Tube Bell ", 14, MIDI_UNMAPPED}, + /*103*/ {"Xylophone ", 13, MIDI_UNMAPPED}, + /*104*/ {"Marimba ", 12, MIDI_UNMAPPED}, + /*105*/ {"Koto ", 107, MIDI_UNMAPPED}, + /*106*/ {"Sho ", 111, MIDI_UNMAPPED}, + /*107*/ {"Shakuhachi", 77, MIDI_UNMAPPED}, + /*108*/ {"Whistle 1 ", 78, MIDI_UNMAPPED}, + /*109*/ {"Whistle 2 ", 78, MIDI_UNMAPPED}, + /*110*/ {"BottleBlow", 76, MIDI_UNMAPPED}, + /*111*/ {"BreathPipe", 121, MIDI_UNMAPPED}, + /*112*/ {"Timpani ", 47, MIDI_UNMAPPED}, + /*113*/ {"MelodicTom", 117, MIDI_UNMAPPED}, + /*114*/ {"Deep Snare", MIDI_MAPPED_TO_RHYTHM, 37}, + /*115*/ {"Elec Perc1", 115, MIDI_UNMAPPED}, /* ? */ + /*116*/ {"Elec Perc2", 118, MIDI_UNMAPPED}, /* ? */ + /*117*/ {"Taiko ", 116, MIDI_UNMAPPED}, + /*118*/ {"Taiko Rim ", 118, MIDI_UNMAPPED}, + /*119*/ {"Cymbal ", MIDI_MAPPED_TO_RHYTHM, 50}, + /*120*/ {"Castanets ", MIDI_UNMAPPED, MIDI_UNMAPPED}, + /*121*/ {"Triangle ", 112, MIDI_UNMAPPED}, + /*122*/ {"Orche Hit ", 55, MIDI_UNMAPPED}, + /*123*/ {"Telephone ", 124, MIDI_UNMAPPED}, + /*124*/ {"Bird Tweet", 123, MIDI_UNMAPPED}, + /*125*/ {"OneNoteJam", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? */ + /*126*/ {"WaterBells", 98, MIDI_UNMAPPED}, + /*127*/ {"JungleTune", MIDI_UNMAPPED, MIDI_UNMAPPED} /* ? */ +}; + +static const Mt32ToGmMap Mt32RhythmTimbreMaps[] = { + /*00*/ {"Acou BD ", MIDI_MAPPED_TO_RHYTHM, 34}, + /*01*/ {"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 37}, + /*02*/ {"Acou HiTom", 117, 49}, + /*03*/ {"AcouMidTom", 117, 46}, + /*04*/ {"AcouLowTom", 117, 40}, + /*05*/ {"Elec SD ", MIDI_MAPPED_TO_RHYTHM, 39}, + /*06*/ {"Clsd HiHat", MIDI_MAPPED_TO_RHYTHM, 41}, + /*07*/ {"OpenHiHat1", MIDI_MAPPED_TO_RHYTHM, 45}, + /*08*/ {"Crash Cym ", MIDI_MAPPED_TO_RHYTHM, 48}, + /*09*/ {"Ride Cym ", MIDI_MAPPED_TO_RHYTHM, 50}, + /*10*/ {"Rim Shot ", MIDI_MAPPED_TO_RHYTHM, 36}, + /*11*/ {"Hand Clap ", MIDI_MAPPED_TO_RHYTHM, 38}, + /*12*/ {"Cowbell ", MIDI_MAPPED_TO_RHYTHM, 55}, + /*13*/ {"Mt HiConga", MIDI_MAPPED_TO_RHYTHM, 61}, + /*14*/ {"High Conga", MIDI_MAPPED_TO_RHYTHM, 62}, + /*15*/ {"Low Conga ", MIDI_MAPPED_TO_RHYTHM, 63}, + /*16*/ {"Hi Timbale", MIDI_MAPPED_TO_RHYTHM, 64}, + /*17*/ {"LowTimbale", MIDI_MAPPED_TO_RHYTHM, 65}, + /*18*/ {"High Bongo", MIDI_MAPPED_TO_RHYTHM, 59}, + /*19*/ {"Low Bongo ", MIDI_MAPPED_TO_RHYTHM, 60}, + /*20*/ {"High Agogo", 113, 66}, + /*21*/ {"Low Agogo ", 113, 67}, + /*22*/ {"Tambourine", MIDI_MAPPED_TO_RHYTHM, 53}, + /*23*/ {"Claves ", MIDI_MAPPED_TO_RHYTHM, 74}, + /*24*/ {"Maracas ", MIDI_MAPPED_TO_RHYTHM, 69}, + /*25*/ {"SmbaWhis L", 78, 71}, + /*26*/ {"SmbaWhis S", 78, 70}, + /*27*/ {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 68}, + /*28*/ {"Quijada ", MIDI_MAPPED_TO_RHYTHM, 72}, + /*29*/ {"OpenHiHat2", MIDI_MAPPED_TO_RHYTHM, 43} +}; + +static const uint8 Mt32PresetRhythmKeymap[] = { + MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, + MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, + MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, + MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, 34, 34, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, MIDI_UNMAPPED, MIDI_UNMAPPED, 53, MIDI_UNMAPPED, 55, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, MIDI_UNMAPPED, 74, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, + MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, + MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, + MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, + MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, + MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED +}; + +/* +++ - Don't change unless you've got a good reason + ++ - Looks good, sounds ok + + - Not too bad, but is it right? + ? - Where do I map this one? + ?? - Any good ideas? + ??? - I'm clueless? + R - Rhythm... */ +static const Mt32ToGmMap Mt32MemoryTimbreMaps[] = { + {"AccPnoKA2 ", 1, MIDI_UNMAPPED}, /* ++ (KQ1) */ + {"Acou BD ", MIDI_MAPPED_TO_RHYTHM, 34}, /* R (PQ2) */ + {"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 37}, /* R (PQ2) */ + {"AcouPnoKA ", 0, MIDI_UNMAPPED}, /* ++ (KQ1) */ + {"BASS ", 32, MIDI_UNMAPPED}, /* + (LSL3) */ + {"BASSOONPCM", 70, MIDI_UNMAPPED}, /* + (CB) */ + {"BEACH WAVE", 122, MIDI_UNMAPPED}, /* + (LSL3) */ + {"BagPipes ", 109, MIDI_UNMAPPED}, + {"BassPizzMS", 45, MIDI_UNMAPPED}, /* ++ (HQ) */ + {"BassoonKA ", 70, MIDI_UNMAPPED}, /* ++ (KQ1) */ + {"Bell MS", 112, MIDI_UNMAPPED}, /* ++ (iceMan) */ + {"Bells MS", 112, MIDI_UNMAPPED}, /* + (HQ) */ + {"Big Bell ", 14, MIDI_UNMAPPED}, /* + (CB) */ + {"Bird Tweet", 123, MIDI_UNMAPPED}, + {"BrsSect MS", 61, MIDI_UNMAPPED}, /* +++ (iceMan) */ + {"CLAPPING ", 126, MIDI_UNMAPPED}, /* ++ (LSL3) */ + {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 68}, /* R (HBoG) */ + {"Calliope ", 82, MIDI_UNMAPPED}, /* +++ (HQ) */ + {"CelticHarp", 46, MIDI_UNMAPPED}, /* ++ (CoC) */ + {"Chicago MS", 1, MIDI_UNMAPPED}, /* ++ (iceMan) */ + {"Chop ", 117, MIDI_UNMAPPED}, + {"Chorale MS", 52, MIDI_UNMAPPED}, /* + (CoC) */ + {"ClarinetMS", 71, MIDI_UNMAPPED}, + {"Claves ", MIDI_MAPPED_TO_RHYTHM, 74}, /* R (PQ2) */ + {"Claw MS", 118, MIDI_UNMAPPED}, /* + (HQ) */ + {"ClockBell ", 14, MIDI_UNMAPPED}, /* + (CB) */ + {"ConcertCym", MIDI_MAPPED_TO_RHYTHM, 54}, /* R ? (KQ1) */ + {"Conga MS", MIDI_MAPPED_TO_RHYTHM, 63}, /* R (HQ) */ + {"CoolPhone ", 124, MIDI_UNMAPPED}, /* ++ (LSL3) */ + {"CracklesMS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */ + {"CreakyD MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */ + {"Cricket ", 120, MIDI_UNMAPPED}, /* ? (CB) */ + {"CrshCymbMS", MIDI_MAPPED_TO_RHYTHM, 56}, /* R +++ (iceMan) */ + {"CstlGateMS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */ + {"CymSwellMS", MIDI_MAPPED_TO_RHYTHM, 54}, /* R ? (CoC, HQ) */ + {"CymbRollKA", MIDI_MAPPED_TO_RHYTHM, 56}, /* R ? (KQ1) */ + {"Cymbal Lo ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* R ? (LSL3) */ + {"card ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HBoG) */ + {"DirtGtr MS", 30, MIDI_UNMAPPED}, /* + (iceMan) */ + {"DirtGtr2MS", 29, MIDI_UNMAPPED}, /* + (iceMan) */ + {"E Bass MS", 33, MIDI_UNMAPPED}, /* + (SQ3) */ + {"ElecBassMS", 33, MIDI_UNMAPPED}, + {"ElecGtr MS", 27, MIDI_UNMAPPED}, /* ++ (iceMan) */ + {"EnglHornMS", 69, MIDI_UNMAPPED}, + {"FantasiaKA", 88, MIDI_UNMAPPED}, + {"Fantasy ", 99, MIDI_UNMAPPED}, /* + (PQ2) */ + {"Fantasy2MS", 99, MIDI_UNMAPPED}, /* ++ (CoC, HQ) */ + {"Filter MS", 95, MIDI_UNMAPPED}, /* +++ (iceMan) */ + {"Filter2 MS", 95, MIDI_UNMAPPED}, /* ++ (iceMan) */ + {"Flame2 MS", 121, MIDI_UNMAPPED}, /* ? (HQ) */ + {"Flames MS", 121, MIDI_UNMAPPED}, /* ? (HQ) */ + {"Flute MS", 73, MIDI_UNMAPPED}, /* +++ (HQ) */ + {"FogHorn MS", 58, MIDI_UNMAPPED}, + {"FrHorn1 MS", 60, MIDI_UNMAPPED}, /* +++ (HQ) */ + {"FunnyTrmp ", 56, MIDI_UNMAPPED}, /* ++ (CB) */ + {"GameSnd MS", 80, MIDI_UNMAPPED}, + {"Glock MS", 9, MIDI_UNMAPPED}, /* +++ (HQ) */ + {"Gunshot ", 127, MIDI_UNMAPPED}, /* +++ (CB) */ + {"Hammer MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */ + {"Harmonica2", 22, MIDI_UNMAPPED}, /* +++ (CB) */ + {"Harpsi 1 ", 6, MIDI_UNMAPPED}, /* + (HBoG) */ + {"Harpsi 2 ", 6, MIDI_UNMAPPED}, /* +++ (CB) */ + {"Heart MS", 116, MIDI_UNMAPPED}, /* ? (iceMan) */ + {"Horse1 MS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */ + {"Horse2 MS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */ + {"InHale MS", 121, MIDI_UNMAPPED}, /* ++ (iceMan) */ + {"KNIFE ", 120, MIDI_UNMAPPED}, /* ? (LSL3) */ + {"KenBanjo ", 105, MIDI_UNMAPPED}, /* +++ (CB) */ + {"Kiss MS", 25, MIDI_UNMAPPED}, /* ++ (HQ) */ + {"KongHit ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */ + {"Koto ", 107, MIDI_UNMAPPED}, /* +++ (PQ2) */ + {"Laser MS", 81, MIDI_UNMAPPED}, /* ?? (HQ) */ + {"Meeps MS", 62, MIDI_UNMAPPED}, /* ? (HQ) */ + {"MTrak MS", 62, MIDI_UNMAPPED}, /* ?? (iceMan) */ + {"MachGun MS", 127, MIDI_UNMAPPED}, /* ? (iceMan) */ + {"OCEANSOUND", 122, MIDI_UNMAPPED}, /* + (LSL3) */ + {"Oboe 2001 ", 68, MIDI_UNMAPPED}, /* + (PQ2) */ + {"Ocean MS", 122, MIDI_UNMAPPED}, /* + (iceMan) */ + {"PPG 2.3 MS", 75, MIDI_UNMAPPED}, /* ? (iceMan) */ + {"PianoCrank", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */ + {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 39}, /* R ? (iceMan) */ + {"PiccoloKA ", 72, MIDI_UNMAPPED}, /* +++ (KQ1) */ + {"PinkBassMS", 39, MIDI_UNMAPPED}, + {"Pizz2 ", 45, MIDI_UNMAPPED}, /* ++ (CB) */ + {"Portcullis", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */ + {"Raspbry MS", 81, MIDI_UNMAPPED}, /* ? (HQ) */ + {"RatSqueek ", 72, MIDI_UNMAPPED}, /* ? (CB, CoC) */ + {"Record78 ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* +++ (CB) */ + {"RecorderMS", 74, MIDI_UNMAPPED}, /* +++ (CoC) */ + {"Red Baron ", 125, MIDI_UNMAPPED}, /* ? (CB) */ + {"ReedPipMS ", 20, MIDI_UNMAPPED}, /* +++ (Coc) */ + {"RevCymb MS", 119, MIDI_UNMAPPED}, + {"RifleShot ", 127, MIDI_UNMAPPED}, /* + (CB) */ + {"RimShot MS", MIDI_MAPPED_TO_RHYTHM, 36}, /* R */ + {"SHOWER ", 52, MIDI_UNMAPPED}, /* ? (LSL3) */ + {"SQ Bass MS", 32, MIDI_UNMAPPED}, /* + (SQ3) */ + {"ShakuVibMS", 79, MIDI_UNMAPPED}, /* + (iceMan) */ + {"SlapBassMS", 36, MIDI_UNMAPPED}, /* +++ (iceMan) */ + {"Snare MS", MIDI_MAPPED_TO_RHYTHM, 37}, /* R (HQ) */ + {"Some Birds", 123, MIDI_UNMAPPED}, /* + (CB) */ + {"Sonar MS", 78, MIDI_UNMAPPED}, /* ? (iceMan) */ + {"Soundtrk2 ", 97, MIDI_UNMAPPED}, /* +++ (CB) */ + {"Soundtrack", 97, MIDI_UNMAPPED}, /* ++ (CoC) */ + {"SqurWaveMS", 80, MIDI_UNMAPPED}, + {"StabBassMS", 34, MIDI_UNMAPPED}, /* + (iceMan) */ + {"SteelDrmMS", 114, MIDI_UNMAPPED}, /* +++ (iceMan) */ + {"StrSect1MS", 48, MIDI_UNMAPPED}, /* ++ (HQ) */ + {"String MS", 45, MIDI_UNMAPPED}, /* + (CoC) */ + {"Syn-Choir ", 91, MIDI_UNMAPPED}, + {"Syn Brass4", 63, MIDI_UNMAPPED}, /* ++ (PQ2) */ + {"SynBass MS", 38, MIDI_UNMAPPED}, + {"SwmpBackgr", 120, MIDI_UNMAPPED}, /* ?? (CB,HQ) */ + {"T-Bone2 MS", 57, MIDI_UNMAPPED}, /* +++ (HQ) */ + {"Taiko ", 116, 34}, /* +++ (Coc) */ + {"Taiko Rim ", 118, 36}, /* +++ (LSL3) */ + {"Timpani1 ", 47, MIDI_UNMAPPED}, /* +++ (CB) */ + {"Tom MS", 117, 47}, /* +++ (iceMan) */ + {"Toms MS", 117, 47}, /* +++ (CoC, HQ) */ + {"Tpt1prtl ", 56, MIDI_UNMAPPED}, /* +++ (KQ1) */ + {"TriangleMS", 112, 80}, /* R (CoC) */ + {"Trumpet 1 ", 56, MIDI_UNMAPPED}, /* +++ (CoC) */ + {"Type MS", 114, MIDI_UNMAPPED}, /* ? (iceMan) */ + {"WaterBells", 98, MIDI_UNMAPPED}, /* + (PQ2) */ + {"WaterFallK", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */ + {"Whiporill ", 123, MIDI_UNMAPPED}, /* + (CB) */ + {"Wind ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */ + {"Wind MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ, iceMan) */ + {"Wind2 MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC) */ + {"Woodpecker", 115, MIDI_UNMAPPED}, /* ? (CB) */ + {"WtrFall MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC, HQ, iceMan) */ + {0, 0, 0} +}; + +} // End of namespace Sci diff --git a/engines/sci/sound/drivers/midi.cpp b/engines/sci/sound/drivers/midi.cpp new file mode 100644 index 0000000000..07d2a3a222 --- /dev/null +++ b/engines/sci/sound/drivers/midi.cpp @@ -0,0 +1,859 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "sci/sci.h" + +#include "common/config-manager.h" +#include "sound/fmopl.h" +#include "sound/softsynth/emumidi.h" + +#include "sci/resource.h" +#include "sci/sound/drivers/mididriver.h" +#include "sci/sound/drivers/map-mt32-to-gm.h" + +namespace Sci { + +class MidiPlayer_Midi : public MidiPlayer { +public: + enum { + kVoices = 32, + kReverbConfigNr = 11, + kMaxSysExSize = 264 + }; + + MidiPlayer_Midi(); + virtual ~MidiPlayer_Midi(); + + int open(ResourceManager *resMan); + void close(); + void send(uint32 b); + void sysEx(const byte *msg, uint16 length); + bool hasRhythmChannel() const { return true; } + byte getPlayId(SciVersion soundVersion); + int getPolyphony() const { return kVoices; } + void setVolume(byte volume); + int getVolume(); + void setReverb(byte reverb); + void playSwitch(bool play); + +private: + bool isMt32GmPatch(const byte *data, int size); + void readMt32GmPatch(const byte *data, int size); + void readMt32Patch(const byte *data, int size); + void readMt32DrvData(); + + void mapMt32ToGm(byte *data, size_t size); + uint8 lookupGmInstrument(const char *iname); + uint8 lookupGmRhythmKey(const char *iname); + uint8 getGmInstrument(const Mt32ToGmMap &Mt32Ins); + + void sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay); + void sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay); + void setMt32Volume(byte volume); + void resetMt32(); + + void noteOn(int channel, int note, int velocity); + void setPatch(int channel, int patch); + void controlChange(int channel, int control, int value); + + struct Channel { + byte mappedPatch; + byte patch; + int velocityMapIdx; + bool playing; + int8 keyShift; + int8 volAdjust; + uint8 pan; + uint8 hold; + uint8 volume; + + Channel() : mappedPatch(MIDI_UNMAPPED), patch(MIDI_UNMAPPED), velocityMapIdx(0), playing(false), + keyShift(0), volAdjust(0), pan(0x80), hold(0), volume(0x7f) { } + }; + + bool _isMt32; + bool _isOldPatchFormat; + bool _hasReverb; + bool _playSwitch; + int _masterVolume; + + byte _reverbConfig[kReverbConfigNr][3]; + Channel _channels[16]; + uint8 _percussionMap[128]; + int8 _keyShift[128]; + int8 _volAdjust[128]; + uint8 _patchMap[128]; + uint8 _velocityMapIdx[128]; + uint8 _velocityMap[4][128]; + + // These are extensions used for our own MT-32 to GM mapping + uint8 _pitchBendRange[128]; + uint8 _percussionVelocityScale[128]; + + byte _goodbyeMsg[20]; + byte _sysExBuf[kMaxSysExSize]; +}; + +MidiPlayer_Midi::MidiPlayer_Midi() : _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) { + MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI); + _driver = createMidi(midiType); + + if (midiType == MD_MT32 || ConfMan.getBool("native_mt32")) + _isMt32 = true; + + _sysExBuf[0] = 0x41; + _sysExBuf[1] = 0x10; + _sysExBuf[2] = 0x16; + _sysExBuf[3] = 0x12; +} + +MidiPlayer_Midi::~MidiPlayer_Midi() { + delete _driver; +} + +void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) { + uint8 patch = _channels[channel].mappedPatch; + + if (channel == MIDI_RHYTHM_CHANNEL) { + if (_percussionMap[note] == MIDI_UNMAPPED) { + debugC(kDebugLevelSound, "[Midi] Percussion instrument %i is unmapped", note); + return; + } + + note = _percussionMap[note]; + // Scale velocity; + velocity = velocity * _percussionVelocityScale[note] / 127; + } else if (patch >= 128) { + if (patch == MIDI_UNMAPPED) + return; + + // Map to rhythm + channel = MIDI_RHYTHM_CHANNEL; + note = patch - 128; + + // Scale velocity; + velocity = velocity * _percussionVelocityScale[note] / 127; + } else { + int8 keyshift = _keyShift[channel]; + + int shiftNote = note + keyshift; + + if (keyshift > 0) { + while (shiftNote > 127) + shiftNote -= 12; + } else { + while (shiftNote < 0) + shiftNote += 12; + } + + note = shiftNote; + + // We assume that velocity 0 maps to 0 (for note off) + int mapIndex = _channels[channel].velocityMapIdx; + velocity = _velocityMap[mapIndex][velocity]; + } + + _channels[channel].playing = true; + _driver->send(0x90 | channel, note, velocity); +} + +void MidiPlayer_Midi::controlChange(int channel, int control, int value) { + switch (control) { + case 0x07: + _channels[channel].volume = value; + + if (!_playSwitch) + return; + + value += _channels[channel].volAdjust; + + if (value > 0x7f) + value = 0x7f; + + if (value < 0) + value = 1; + + value *= _masterVolume; + + if (value != 0) { + value /= 15; + + if (value == 0) + value = 1; + } + break; + case 0x0a: + if (_channels[channel].pan == value) + return; + + _channels[channel].pan = value; + break; + case 0x40: + if (_channels[channel].hold == value) + return; + + _channels[channel].hold = value; + break; + case 0x7b: + if (!_channels[channel].playing) + return; + + _channels[channel].playing = false; + } + + _driver->send(0xb0 | channel, control, value); +} + +void MidiPlayer_Midi::setPatch(int channel, int patch) { + bool resetVol = false; + + if ((channel == MIDI_RHYTHM_CHANNEL) || (_channels[channel].patch == patch)) + return; + + _channels[channel].patch = patch; + _channels[channel].velocityMapIdx = _velocityMapIdx[patch]; + + if (_channels[channel].mappedPatch == MIDI_UNMAPPED) + resetVol = true; + + _channels[channel].mappedPatch = _patchMap[patch]; + + if (_patchMap[patch] == MIDI_UNMAPPED) { + debugC(kDebugLevelSound, "[Midi] Channel %i set to unmapped patch %i", channel, patch); + _driver->send(0xb0 | channel, 0x7b, 0); + _driver->send(0xb0 | channel, 0x40, 0); + return; + } + + if (_channels[channel].keyShift != _keyShift[patch]) { + _channels[channel].keyShift = _keyShift[patch]; + _driver->send(0xb0 | channel, 0x7b, 0); + _driver->send(0xb0 | channel, 0x40, 0); + resetVol = true; + } + + if (resetVol || (_channels[channel].volAdjust != _volAdjust[patch])) { + _channels[channel].volAdjust = _volAdjust[patch]; + controlChange(channel, 0x07, _channels[channel].volume); + } + + uint8 bendRange = _pitchBendRange[patch]; + if (bendRange != MIDI_UNMAPPED) + _driver->setPitchBendRange(channel, bendRange); + + _driver->send(0xc0 | channel, _patchMap[patch], 0); +} + +void MidiPlayer_Midi::send(uint32 b) { + byte command = b & 0xf0; + byte channel = b & 0xf; + byte op1 = (b >> 8) & 0x7f; + byte op2 = (b >> 16) & 0x7f; + + // In early SCI0, we may also get events for AdLib rhythm channels. + // While an MT-32 would ignore those with the default channel mapping, + // we filter these out for the benefit of other MIDI devices. + if (channel < 1 || channel > 9) + return; + + switch (command) { + case 0x80: + noteOn(channel, op1, 0); + break; + case 0x90: + noteOn(channel, op1, op2); + break; + case 0xb0: + controlChange(channel, op1, op2); + break; + case 0xc0: + setPatch(channel, op1); + break; + case 0xe0: + _driver->send(b); + break; + default: + warning("Ignoring MIDI event %02x", command); + } +} + +void MidiPlayer_Midi::setVolume(byte volume) { + _masterVolume = volume; + + if (!_playSwitch) + return; + + for (uint i = 1; i < 10; i++) { + if (_channels[i].volume != 0xff) + controlChange(i, 0x07, _channels[i].volume & 0x7f); + } +} + +int MidiPlayer_Midi::getVolume() { + return _masterVolume; +} + +void MidiPlayer_Midi::setReverb(byte reverb) { + _reverb = CLIP(reverb, 0, kReverbConfigNr - 1); + if (_hasReverb) + sendMt32SysEx(0x100001, _reverbConfig[_reverb], 3, true); +} + +void MidiPlayer_Midi::playSwitch(bool play) { + _playSwitch = play; + if (play) + setVolume(_masterVolume); + else { + for (uint i = 1; i < 10; i++) + _driver->send(0xb0 | i, 7, 0); + } +} + +bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) +{ + if (size < 1155) + return false; + if (size > 16889) + return true; + + bool isMt32 = false; + bool isMt32Gm = false; + + if (READ_LE_UINT16(data + 1153) + 1155 == size) + isMt32Gm = true; + + int pos = 492 + 246 * data[491]; + + if ((size >= (pos + 386)) && (READ_BE_UINT16(data + pos) == 0xabcd)) + pos += 386; + + if ((size >= (pos + 267)) && (READ_BE_UINT16(data + pos) == 0xdcba)) + pos += 267; + + if (size == pos) + isMt32 = true; + + if (isMt32 == isMt32Gm) + error("Failed to detect MT-32 patch format"); + + return isMt32Gm; +} + +void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay = false) { + if (len + 8 > kMaxSysExSize) { + warning("SysEx message exceed maximum size; ignoring"); + return; + } + + uint16 chk = 0; + + _sysExBuf[4] = (addr >> 16) & 0xff; + _sysExBuf[5] = (addr >> 8) & 0xff; + _sysExBuf[6] = addr & 0xff; + + for (int i = 0; i < len; i++) + _sysExBuf[7 + i] = str->readByte(); + + for (int i = 4; i < 7 + len; i++) + chk += _sysExBuf[i]; + + _sysExBuf[7 + len] = 128 - chk % 128; + + if (noDelay) + _driver->sysEx(_sysExBuf, len + 8); + else + sysEx(_sysExBuf, len + 8); +} + +void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay = false) { + Common::MemoryReadStream *str = new Common::MemoryReadStream(buf, len); + sendMt32SysEx(addr, str, len, noDelay); + delete str; +} + +void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) { + Common::MemoryReadStream *str = new Common::MemoryReadStream(data, size); + + // Send before-SysEx text + str->seek(0x14); + sendMt32SysEx(0x200000, str, 20); + + // Save goodbye message + str->read(_goodbyeMsg, 20); + + byte volume = CLIP(str->readUint16LE(), 0, 100); + setMt32Volume(volume); + + // Reverb default only used in (roughly) SCI0/SCI01 + _reverb = str->readByte(); + _hasReverb = true; + + // Skip reverb SysEx message + str->seek(11, SEEK_CUR); + + // Read reverb data + for (int i = 0; i < kReverbConfigNr; i++) { + _reverbConfig[i][0] = str->readByte(); + _reverbConfig[i][1] = str->readByte(); + _reverbConfig[i][2] = str->readByte(); + } + + // Patches 1-48 + sendMt32SysEx(0x50000, str, 256); + sendMt32SysEx(0x50200, str, 128); + + // Timbres + byte timbresNr = str->readByte(); + for (int i = 0; i < timbresNr; i++) + sendMt32SysEx(0x80000 + (i << 9), str, 246); + + uint16 flag = str->readUint16BE(); + + if (!str->eos() && (flag == 0xabcd)) { + // Patches 49-96 + sendMt32SysEx(0x50300, str, 256); + sendMt32SysEx(0x50500, str, 128); + flag = str->readUint16BE(); + } + + if (!str->eos() && (flag == 0xdcba)) { + // Rhythm key map + sendMt32SysEx(0x30110, str, 256); + // Partial reserve + sendMt32SysEx(0x100004, str, 9); + } + + // Send after-SysEx text + str->seek(0); + sendMt32SysEx(0x200000, str, 20); + + // Send the mystery SysEx + sendMt32SysEx(0x52000a, (const byte *)"\x16\x16\x16\x16\x16\x16", 6); + + delete str; +} + +void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) { + memcpy(_patchMap, data, 0x80); + memcpy(_keyShift, data + 0x80, 0x80); + memcpy(_volAdjust, data + 0x100, 0x80); + memcpy(_percussionMap, data + 0x180, 0x80); + _channels[MIDI_RHYTHM_CHANNEL].volAdjust = data[0x200]; + memcpy(_velocityMapIdx, data + 0x201, 0x80); + memcpy(_velocityMap, data + 0x281, 0x200); + + uint16 midiSize = READ_LE_UINT16(data + 0x481); + + if (midiSize > 0) { + if (size < midiSize + 1155) + error("Failed to read MIDI data"); + + const byte *midi = data + 1155; + byte command = 0; + uint i = 0; + + while (i < midiSize) { + byte op1, op2; + + if (midi[i] & 0x80) + command = midi[i++]; + + switch (command & 0xf0) { + case 0xf0: { + byte *sysExEnd = (byte *)memchr(midi + i, 0xf7, midiSize - i); + + if (!sysExEnd) + error("Failed to find end of sysEx"); + + int len = sysExEnd - (midi + i); + sysEx(midi + i, len); + + i += len + 1; // One more for the 0x7f + break; + } + case 0x80: + case 0x90: + case 0xa0: + case 0xb0: + case 0xe0: + if (i + 1 >= midiSize) + error("MIDI command exceeds data size"); + + op1 = midi[i++]; + op2 = midi[i++]; + _driver->send(command, op1, op2); + break; + case 0xc0: + case 0xd0: + if (i >= midiSize) + error("MIDI command exceeds data size"); + + op1 = midi[i++]; + _driver->send(command, op1, 0); + break; + default: + error("Failed to find MIDI command byte"); + } + } + } +} + +void MidiPlayer_Midi::readMt32DrvData() { + Common::File f; + + if (f.open("MT32.DRV")) { + int size = f.size(); + + assert(size >= 166); + + // Send before-SysEx text + f.seek(0x59); + + // Skip 2 extra 0 bytes in some drivers + if (f.readUint16LE() != 0) + f.seek(-2, SEEK_CUR); + + sendMt32SysEx(0x200000, static_cast(&f), 20); + + // Send after-SysEx text (SSCI sends this before every song) + sendMt32SysEx(0x200000, static_cast(&f), 20); + + // Save goodbye message + f.read(_goodbyeMsg, 20); + + // Set volume + byte volume = CLIP(f.readUint16LE(), 0, 100); + setMt32Volume(volume); + + byte reverbSysEx[13]; + // This old driver should have a full reverb SysEx + if ((f.read(reverbSysEx, 13) != 13) || (reverbSysEx[0] != 0xf0) || (reverbSysEx[12] != 0xf7)) + error("Error reading MT32.DRV"); + + // Send reverb SysEx + sysEx(reverbSysEx + 1, 11); + _hasReverb = false; + + f.seek(0x29); + + // Read AdLib->MT-32 patch map + for (int i = 0; i < 48; i++) { + _patchMap[i] = f.readByte(); + } + + f.close(); + } else { + error("Failed to open MT32.DRV"); + } +} + +byte MidiPlayer_Midi::lookupGmInstrument(const char *iname) { + int i = 0; + + while (Mt32MemoryTimbreMaps[i].name) { + if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0) + return getGmInstrument(Mt32MemoryTimbreMaps[i]); + i++; + } + return MIDI_UNMAPPED; +} + +byte MidiPlayer_Midi::lookupGmRhythmKey(const char *iname) { + int i = 0; + + while (Mt32MemoryTimbreMaps[i].name) { + if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0) + return Mt32MemoryTimbreMaps[i].gmRhythmKey; + i++; + } + return MIDI_UNMAPPED; +} + +uint8 MidiPlayer_Midi::getGmInstrument(const Mt32ToGmMap &Mt32Ins) { + if (Mt32Ins.gmInstr == MIDI_MAPPED_TO_RHYTHM) + return Mt32Ins.gmRhythmKey + 0x80; + else + return Mt32Ins.gmInstr; +} + +void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) { + // FIXME: Clean this up + int memtimbres, patches; + uint8 group, number, keyshift, finetune, bender_range; + uint8 *patchpointer; + uint32 pos; + int i; + + for (i = 0; i < 128; i++) { + _patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[i]); + _pitchBendRange[i] = 12; + } + + for (i = 0; i < 128; i++) + _percussionMap[i] = Mt32PresetRhythmKeymap[i]; + + memtimbres = *(data + 0x1eb); + pos = 0x1ec + memtimbres * 0xf6; + + if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xabcd)) { + patches = 96; + pos += 2 + 8 * 48; + } else + patches = 48; + + debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Patches detected", patches); + debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Memory Timbres", memtimbres); + + debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping patches.."); + + for (i = 0; i < patches; i++) { + char name[11]; + + if (i < 48) + patchpointer = data + 0x6b + 8 * i; + else + patchpointer = data + 0x1ec + 8 * (i - 48) + memtimbres * 0xf6 + 2; + + group = *patchpointer; + number = *(patchpointer + 1); + keyshift = *(patchpointer + 2); + finetune = *(patchpointer + 3); + bender_range = *(patchpointer + 4); + + debugCN(kDebugLevelSound, " [%03d] ", i); + + switch (group) { + case 1: + number += 64; + // Fall through + case 0: + _patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[number]); + debugCN(kDebugLevelSound, "%s -> ", Mt32PresetTimbreMaps[number].name); + break; + case 2: + strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10); + name[10] = 0; + _patchMap[i] = lookupGmInstrument(name); + debugCN(kDebugLevelSound, "%s -> ", name); + break; + case 3: + _patchMap[i] = getGmInstrument(Mt32RhythmTimbreMaps[number]); + debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number].name); + break; + default: + break; + } + + if (_patchMap[i] == MIDI_UNMAPPED) { + debugC(kDebugLevelSound, "[Unmapped]"); + } else { + if (_patchMap[i] >= 128) { + debugC(kDebugLevelSound, "%s [Rhythm]", GmPercussionNames[_patchMap[i] - 128]); + } else { + debugC(kDebugLevelSound, "%s", GmInstrumentNames[_patchMap[i]]); + } + } + + _keyShift[i] = CLIP(keyshift, 0, 48) - 24; + _pitchBendRange[i] = CLIP(bender_range, 0, 24); + } + + if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xdcba)) { + debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping percussion.."); + + for (i = 0; i < 64 ; i++) { + number = *(data + pos + 4 * i + 2); + + debugCN(kDebugLevelSound, " [%03d] ", i + 23); + + if (number < 64) { + char name[11]; + strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10); + name[10] = 0; + debugCN(kDebugLevelSound, "%s -> ", name); + _percussionMap[i + 23] = lookupGmRhythmKey(name); + } else { + if (number < 94) { + debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number - 64].name); + _percussionMap[i + 23] = Mt32RhythmTimbreMaps[number - 64].gmRhythmKey; + } else { + debugCN(kDebugLevelSound, "[Key %03i] -> ", number); + _percussionMap[i + 23] = MIDI_UNMAPPED; + } + } + + if (_percussionMap[i + 23] == MIDI_UNMAPPED) + debugC(kDebugLevelSound, "[Unmapped]"); + else + debugC(kDebugLevelSound, "%s", GmPercussionNames[_percussionMap[i + 23]]); + + _percussionVelocityScale[i + 23] = *(data + pos + 4 * i + 3) * 127 / 100; + } + } +} + +void MidiPlayer_Midi::setMt32Volume(byte volume) { + sendMt32SysEx(0x100016, &volume, 1); +} + +void MidiPlayer_Midi::resetMt32() { + sendMt32SysEx(0x7f0000, (const byte *)"\x01\x00", 2, true); + + // This seems to require a longer delay than usual + g_system->delayMillis(150); +} + +int MidiPlayer_Midi::open(ResourceManager *resMan) { + assert(resMan != NULL); + + int retval = _driver->open(); + if (retval != 0) { + warning("Failed to open MIDI driver"); + return retval; + } + + // By default use no mapping + for (uint i = 0; i < 128; i++) { + _percussionMap[i] = i; + _patchMap[i] = i; + _velocityMap[0][i] = i; + _keyShift[i] = 0; + _volAdjust[i] = 0; + _velocityMapIdx[i] = 0; + _pitchBendRange[i] = MIDI_UNMAPPED; + _percussionVelocityScale[i] = 127; + } + + Resource *res = NULL; + + if (_isMt32) { + // MT-32 + resetMt32(); + + res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); + + if (res) { + if (isMt32GmPatch(res->data, res->size)) { + readMt32GmPatch(res->data, res->size); + strncpy((char *)_goodbyeMsg, " ScummVM ", 20); + } else { + readMt32Patch(res->data, res->size); + } + } else { + readMt32DrvData(); + } + } else { + // General MIDI + res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0); + + if (res && isMt32GmPatch(res->data, res->size)) { + // There is a GM patch + readMt32GmPatch(res->data, res->size); + + // Detect the format of patch 1, so that we know what play mask to use + res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); + if (!res) + _isOldPatchFormat = false; + else + _isOldPatchFormat = !isMt32GmPatch(res->data, res->size); + } else { + // No GM patch found, map instruments using MT-32 patch + + warning("Game has no native support for General MIDI, applying auto-mapping"); + + // Modify velocity map to make low velocity notes a little louder + for (uint i = 1; i < 0x40; i++) + _velocityMap[0][i] = 0x20 + (i - 1) / 2; + + res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); + + if (res) { + if (!isMt32GmPatch(res->data, res->size)) + mapMt32ToGm(res->data, res->size); + else + error("MT-32 patch has wrong type"); + } else { + // No MT-32 patch present, try to read from MT32.DRV + Common::File f; + + if (f.open("MT32.DRV")) { + int size = f.size(); + + assert(size >= 70); + + f.seek(0x29); + + // Read AdLib->MT-32 patch map + for (int i = 0; i < 48; i++) + _patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[f.readByte() & 0x7f]); + } + } + } + } + + return 0; +} + +void MidiPlayer_Midi::close() { + if (_isMt32) { + // Send goodbye message + sendMt32SysEx(0x200000, _goodbyeMsg, 20); + } + + _driver->close(); +} + +void MidiPlayer_Midi::sysEx(const byte *msg, uint16 length) { + _driver->sysEx(msg, length); + + // Wait the time it takes to send the SysEx data + uint32 delay = (length + 2) * 1000 / 3125; + + // Plus an additional delay for the MT-32 rev00 + if (_isMt32) + delay += 40; + + g_system->delayMillis(delay); + g_system->updateScreen(); +} + +byte MidiPlayer_Midi::getPlayId(SciVersion soundVersion) { + switch (soundVersion) { + case SCI_VERSION_0_EARLY: + case SCI_VERSION_0_LATE: + return 0x01; + default: + if (_isMt32) + return 0x0c; + else + return _isOldPatchFormat ? 0x0c : 0x07; + } +} + +MidiPlayer *MidiPlayer_Midi_create() { + return new MidiPlayer_Midi(); +} + +} // End of namespace Sci diff --git a/engines/sci/sound/drivers/mididriver.h b/engines/sci/sound/drivers/mididriver.h new file mode 100644 index 0000000000..a1e6390a06 --- /dev/null +++ b/engines/sci/sound/drivers/mididriver.h @@ -0,0 +1,120 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SCI_SFX_SOFTSEQ_MIDIDRIVER_H +#define SCI_SFX_SOFTSEQ_MIDIDRIVER_H + +#include "sci/sci.h" +#include "sound/mididrv.h" +#include "sound/softsynth/emumidi.h" +#include "common/error.h" + +namespace Sci { + +class ResourceManager; + +enum { + MIDI_CHANNELS = 16, + MIDI_PROP_MASTER_VOLUME = 0 +}; + + +#define MIDI_RHYTHM_CHANNEL 9 + +/* Special SCI sound stuff */ + +#define SCI_MIDI_TIME_EXPANSION_PREFIX 0xF8 +#define SCI_MIDI_TIME_EXPANSION_LENGTH 240 + +#define SCI_MIDI_EOT 0xFC +#define SCI_MIDI_SET_SIGNAL 0xCF +#define SCI_MIDI_SET_POLYPHONY 0x4B +#define SCI_MIDI_RESET_ON_SUSPEND 0x4C +#define SCI_MIDI_CHANNEL_MUTE 0x4E +#define SCI_MIDI_SET_REVERB 0x50 +#define SCI_MIDI_HOLD 0x52 +#define SCI_MIDI_CUMULATIVE_CUE 0x60 +#define SCI_MIDI_CHANNEL_SOUND_OFF 0x78 /* all-sound-off for Bn */ +#define SCI_MIDI_CHANNEL_NOTES_OFF 0x7B /* all-notes-off for Bn */ + +#define SCI_MIDI_SET_SIGNAL_LOOP 0x7F +/* If this is the parameter of 0xCF, the loop point is set here */ + +#define SCI_MIDI_CONTROLLER(status) ((status & 0xF0) == 0xB0) + +class MidiPlayer : public MidiDriver { +protected: + MidiDriver *_driver; + byte _reverb; + +public: + MidiPlayer() : _reverb(0) { } + + int open() { + ResourceManager *resMan = ((SciEngine *)g_engine)->getResourceManager(); // HACK + return open(resMan); + } + virtual int open(ResourceManager *resMan) { return _driver->open(); } + virtual void close() { _driver->close(); } + virtual void send(uint32 b) { _driver->send(b); } + uint32 getBaseTempo() { return _driver->getBaseTempo(); } + virtual bool hasRhythmChannel() const = 0; + MidiChannel *allocateChannel() { return _driver->allocateChannel(); } + MidiChannel *getPercussionChannel() { return _driver->getPercussionChannel(); } + void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _driver->setTimerCallback(timer_param, timer_proc); } + + virtual byte getPlayId(SciVersion soundVersion) = 0; + virtual int getPolyphony() const = 0; + + virtual void setVolume(byte volume) { + if(_driver) + _driver->property(MIDI_PROP_MASTER_VOLUME, volume); + } + + virtual int getVolume() { + return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0; + } + + virtual byte getReverb() { return _reverb; } + virtual void setReverb(byte reverb) { _reverb = reverb; } + + virtual void playSwitch(bool play) { + if (!play) { + // Send "All Sound Off" on all channels + for (int i = 0; i < MIDI_CHANNELS; ++i) + _driver->send(0xb0 + i, SCI_MIDI_CHANNEL_NOTES_OFF, 0); + } + } +}; + +extern MidiPlayer *MidiPlayer_AdLib_create(); +extern MidiPlayer *MidiPlayer_Amiga_create(); +extern MidiPlayer *MidiPlayer_PCJr_create(); +extern MidiPlayer *MidiPlayer_PCSpeaker_create(); +extern MidiPlayer *MidiPlayer_Midi_create(); + +} // End of namespace Sci + +#endif // SCI_SFX_SOFTSEQ_MIDIDRIVER_H diff --git a/engines/sci/sound/drivers/pcjr.cpp b/engines/sci/sound/drivers/pcjr.cpp new file mode 100644 index 0000000000..eb264fb9dd --- /dev/null +++ b/engines/sci/sound/drivers/pcjr.cpp @@ -0,0 +1,277 @@ +/* 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. + * + * $URL$ + * $Id$ + * + */ + +#include "sci/sound/drivers/mididriver.h" + +namespace Sci { + +#define VOLUME_SHIFT 3 + +#define BASE_NOTE 129 // A10 +#define BASE_OCTAVE 10 // A10, as I said + +const static int freq_table[12] = { // A4 is 440Hz, halftone map is x |-> ** 2^(x/12) + 28160, // A10 + 29834, + 31608, + 33488, + 35479, + 37589, + 39824, + 42192, + 44701, + 47359, + 50175, + 53159 +}; + +static inline int get_freq(int note) { + int halftone_delta = note - BASE_NOTE; + int oct_diff = ((halftone_delta + BASE_OCTAVE * 12) / 12) - BASE_OCTAVE; + int halftone_index = (halftone_delta + (12 * 100)) % 12 ; + int freq = (!note) ? 0 : freq_table[halftone_index] / (1 << (-oct_diff)); + + return freq; +} + +class MidiDriver_PCJr : public MidiDriver_Emulated { +public: + friend class MidiPlayer_PCJr; + + enum { + kMaxChannels = 3 + }; + + MidiDriver_PCJr(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { } + ~MidiDriver_PCJr() { } + + // MidiDriver + int open() { return open(kMaxChannels); } + void close(); + void send(uint32 b); + MidiChannel *allocateChannel() { return NULL; } + MidiChannel *getPercussionChannel() { return NULL; } + + // AudioStream + bool isStereo() const { return false; } + int getRate() const { return _mixer->getOutputRate(); } + + // MidiDriver_Emulated + void generateSamples(int16 *buf, int len); + + int open(int channels); +private: + int _channels_nr; + int _global_volume; // Base volume + int _volumes[kMaxChannels]; + int _notes[kMaxChannels]; // Current halftone, or 0 if off + int _freq_count[kMaxChannels]; + int _channel_assigner; + int _channels_assigned; + int _chan_nrs[kMaxChannels]; +}; + +void MidiDriver_PCJr::send(uint32 b) { + byte command = b & 0xff; + byte op1 = (b >> 8) & 0xff; + byte op2 = (b >> 16) & 0xff; + int i; + int mapped_chan = -1; + int chan_nr = command & 0xf; + + // First, test for channel having been assigned already + if (_channels_assigned & (1 << chan_nr)) { + // Already assigned this channel number: + for (i = 0; i < _channels_nr; i++) + if (_chan_nrs[i] == chan_nr) { + mapped_chan = i; + break; + } + } else if ((command & 0xe0) == 0x80) { + // Assign new channel round-robin + + // Mark channel as unused: + if (_chan_nrs[_channel_assigner] >= 0) + _channels_assigned &= ~(1 << _chan_nrs[_channel_assigner]); + + // Remember channel: + _chan_nrs[_channel_assigner] = chan_nr; + // Mark channel as used + _channels_assigned |= (1 << _chan_nrs[_channel_assigner]); + + // Save channel for use later in this call: + mapped_chan = _channel_assigner; + // Round-ropin iterate channel assigner: + _channel_assigner = (_channel_assigner + 1) % _channels_nr; + } + + if (mapped_chan == -1) + return; + + switch (command & 0xf0) { + + case 0x80: + if (op1 == _notes[mapped_chan]) + _notes[mapped_chan] = 0; + break; + + case 0x90: + if (!op2) { + if (op1 == _notes[mapped_chan]) + _notes[mapped_chan] = 0; + } else { + _notes[mapped_chan] = op1; + _volumes[mapped_chan] = op2; + } + break; + + case 0xb0: + if ((op1 == SCI_MIDI_CHANNEL_NOTES_OFF) || (op1 == SCI_MIDI_CHANNEL_SOUND_OFF)) + _notes[mapped_chan] = 0; + break; + + default: + debug(2, "Unused MIDI command %02x %02x %02x", command, op1, op2); + break; /* ignore */ + } +} + +void MidiDriver_PCJr::generateSamples(int16 *data, int len) { + int i; + int chan; + int freq[kMaxChannels]; + int frequency = getRate(); + + for (chan = 0; chan < _channels_nr; chan++) + freq[chan] = get_freq(_notes[chan]); + + for (i = 0; i < len; i++) { + int16 result = 0; + + for (chan = 0; chan < _channels_nr; chan++) + if (_notes[chan]) { + int volume = (_global_volume * _volumes[chan]) + >> VOLUME_SHIFT; + + _freq_count[chan] += freq[chan]; + while (_freq_count[chan] >= (frequency << 1)) + _freq_count[chan] -= (frequency << 1); + + if (_freq_count[chan] - freq[chan] < 0) { + /* Unclean rising edge */ + int l = volume << 1; + result += -volume + (l * _freq_count[chan]) / freq[chan]; + } else if (_freq_count[chan] >= frequency + && _freq_count[chan] - freq[chan] < frequency) { + /* Unclean falling edge */ + int l = volume << 1; + result += volume - (l * (_freq_count[chan] - frequency)) / freq[chan]; + } else { + if (_freq_count[chan] < frequency) + result += volume; + else + result += -volume; + } + } + data[i] = result; + } +} + +int MidiDriver_PCJr::open(int channels) { + if (_isOpen) + return MERR_ALREADY_OPEN; + + if (channels > kMaxChannels) + return -1; + + _channels_nr = channels; + _global_volume = 100; + for (int i = 0; i < _channels_nr; i++) { + _volumes[i] = 100; + _notes[i] = 0; + _freq_count[i] = 0; + _chan_nrs[i] = -1; + } + _channel_assigner = 0; + _channels_assigned = 0; + + MidiDriver_Emulated::open(); + + _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); + + return 0; +} + +void MidiDriver_PCJr::close() { + _mixer->stopHandle(_mixerSoundHandle); +} + +class MidiPlayer_PCJr : public MidiPlayer { +public: + MidiPlayer_PCJr() { _driver = new MidiDriver_PCJr(g_system->getMixer()); } + int open(ResourceManager *resMan) { return static_cast(_driver)->open(getPolyphony()); } + byte getPlayId(SciVersion soundVersion); + int getPolyphony() const { return 3; } + bool hasRhythmChannel() const { return false; } + void setVolume(byte volume) { static_cast(_driver)->_global_volume = volume; } +}; + +byte MidiPlayer_PCJr::getPlayId(SciVersion soundVersion) { + switch (soundVersion) { + case SCI_VERSION_0_EARLY: + return 0x02; + case SCI_VERSION_0_LATE: + return 0x10; + default: + return 0x13; + } +} + +MidiPlayer *MidiPlayer_PCJr_create() { + return new MidiPlayer_PCJr(); +} + +class MidiPlayer_PCSpeaker : public MidiPlayer_PCJr { +public: + byte getPlayId(SciVersion soundVersion); + int getPolyphony() const { return 1; } +}; + +byte MidiPlayer_PCSpeaker::getPlayId(SciVersion soundVersion) { + switch (soundVersion) { + case SCI_VERSION_0_EARLY: + return 0x04; + case SCI_VERSION_0_LATE: + return 0x20; + default: + return 0x12; + } +} + +MidiPlayer *MidiPlayer_PCSpeaker_create() { + return new MidiPlayer_PCSpeaker(); +} + +} // End of namespace Sci diff --git a/engines/sci/sound/iterator/core.cpp b/engines/sci/sound/iterator/core.cpp index 1a53333e4e..c2c110a121 100644 --- a/engines/sci/sound/iterator/core.cpp +++ b/engines/sci/sound/iterator/core.cpp @@ -30,7 +30,7 @@ #include "sci/sound/iterator/core.h" #include "sci/sound/iterator/iterator.h" -#include "sci/sound/softseq/mididriver.h" +#include "sci/sound/drivers/mididriver.h" #include "common/system.h" #include "common/timer.h" diff --git a/engines/sci/sound/iterator/iterator.h b/engines/sci/sound/iterator/iterator.h index 92a619d80e..e5c8f50702 100644 --- a/engines/sci/sound/iterator/iterator.h +++ b/engines/sci/sound/iterator/iterator.h @@ -31,7 +31,7 @@ #include "sci/sci.h" // for USE_OLD_MUSIC_FUNCTIONS #ifdef USE_OLD_MUSIC_FUNCTIONS -#include "sci/sound/softseq/mididriver.h" +#include "sci/sound/drivers/mididriver.h" namespace Audio { class AudioStream; diff --git a/engines/sci/sound/iterator/iterator_internal.h b/engines/sci/sound/iterator/iterator_internal.h index 8eb35d7a40..5a0f0d3ec9 100644 --- a/engines/sci/sound/iterator/iterator_internal.h +++ b/engines/sci/sound/iterator/iterator_internal.h @@ -30,7 +30,7 @@ #ifdef USE_OLD_MUSIC_FUNCTIONS #include "sci/sound/iterator/iterator.h" -#include "sci/sound/softseq/mididriver.h" +#include "sci/sound/drivers/mididriver.h" #include "common/array.h" #include "common/list.h" diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index f8f6b5754d..93586fbab6 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -26,7 +26,7 @@ #include "sci/engine/kernel.h" #include "sci/engine/state.h" #include "sci/sound/midiparser_sci.h" -#include "sci/sound/softseq/mididriver.h" +#include "sci/sound/drivers/mididriver.h" namespace Sci { diff --git a/engines/sci/sound/music.h b/engines/sci/sound/music.h index 26cf3ac0ab..2c3f2d96a6 100644 --- a/engines/sci/sound/music.h +++ b/engines/sci/sound/music.h @@ -37,7 +37,7 @@ #include "sci/sci.h" #include "sci/resource.h" -#include "sci/sound/softseq/mididriver.h" +#include "sci/sound/drivers/mididriver.h" namespace Sci { diff --git a/engines/sci/sound/softseq/adlib.cpp b/engines/sci/sound/softseq/adlib.cpp deleted file mode 100644 index 9534b5bad0..0000000000 --- a/engines/sci/sound/softseq/adlib.cpp +++ /dev/null @@ -1,841 +0,0 @@ -/* 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. - * - * $URL$ - * $Id$ - * - */ - -#include "sci/sci.h" - -#include "sound/fmopl.h" -#include "sound/softsynth/emumidi.h" - -#include "sci/resource.h" -#include "sci/sound/softseq/mididriver.h" - -namespace Sci { - -#ifdef __DC__ -#define STEREO false -#else -#define STEREO true -#endif - -// FIXME: We don't seem to be sending the polyphony init data, so disable this for now -#define ADLIB_DISABLE_VOICE_MAPPING - -class MidiDriver_AdLib : public MidiDriver_Emulated { -public: - enum { - kVoices = 9, - kRhythmKeys = 62 - }; - - MidiDriver_AdLib(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15), _rhythmKeyMap(0), _opl(0) { } - virtual ~MidiDriver_AdLib() { } - - // MidiDriver - int open(bool isSCI0); - void close(); - void send(uint32 b); - MidiChannel *allocateChannel() { return NULL; } - MidiChannel *getPercussionChannel() { return NULL; } - - // AudioStream - bool isStereo() const { return _stereo; } - int getRate() const { return _mixer->getOutputRate(); } - - // MidiDriver_Emulated - void generateSamples(int16 *buf, int len); - - void setVolume(byte volume); - void playSwitch(bool play); - bool loadResource(const byte *data, uint size); - virtual uint32 property(int prop, uint32 param); - -private: - enum ChannelID { - kLeftChannel = 1, - kRightChannel = 2 - }; - - struct AdLibOperator { - bool amplitudeMod; - bool vibrato; - bool envelopeType; - bool kbScaleRate; - byte frequencyMult; // (0-15) - byte kbScaleLevel; // (0-3) - byte totalLevel; // (0-63, 0=max, 63=min) - byte attackRate; // (0-15) - byte decayRate; // (0-15) - byte sustainLevel; // (0-15) - byte releaseRate; // (0-15) - byte waveForm; // (0-3) - }; - - struct AdLibModulator { - byte feedback; // (0-7) - bool algorithm; - }; - - struct AdLibPatch { - AdLibOperator op[2]; - AdLibModulator mod; - }; - - struct Channel { - uint8 patch; // Patch setting - uint8 volume; // Channel volume (0-63) - uint8 pan; // Pan setting (0-127, 64 is center) - uint8 holdPedal; // Hold pedal setting (0 to 63 is off, 127 to 64 is on) - uint8 extraVoices; // The number of additional voices this channel optimally needs - uint16 pitchWheel; // Pitch wheel setting (0-16383, 8192 is center) - uint8 lastVoice; // Last voice used for this MIDI channel - bool enableVelocity; // Enable velocity control (SCI0) - - Channel() : patch(0), volume(63), pan(64), holdPedal(0), extraVoices(0), - pitchWheel(8192), lastVoice(0), enableVelocity(false) { } - }; - - struct AdLibVoice { - int8 channel; // MIDI channel that this voice is assigned to or -1 - int8 note; // Currently playing MIDI note or -1 - int patch; // Currently playing patch or -1 - uint8 velocity; // Note velocity - bool isSustained; // Flag indicating a note that is being sustained by the hold pedal - uint16 age; // Age of the current note - - AdLibVoice() : channel(-1), note(-1), patch(-1), velocity(0), isSustained(false), age(0) { } - }; - - bool _stereo; - bool _isSCI0; - OPL::OPL *_opl; - bool _playSwitch; - int _masterVolume; - Channel _channels[MIDI_CHANNELS]; - AdLibVoice _voices[kVoices]; - byte *_rhythmKeyMap; - Common::Array _patches; - - void loadInstrument(const byte *ins); - void voiceOn(int voice, int note, int velocity); - void voiceOff(int voice); - void setPatch(int voice, int patch); - void setNote(int voice, int note, bool key); - void setVelocity(int voice); - void setOperator(int oper, AdLibOperator &op); - void setRegister(int reg, int value, int channels = kLeftChannel | kRightChannel); - void renewNotes(int channel, bool key); - void noteOn(int channel, int note, int velocity); - void noteOff(int channel, int note); - int findVoice(int channel); - void voiceMapping(int channel, int voices); - void assignVoices(int channel, int voices); - void releaseVoices(int channel, int voices); - void donateVoices(); - int findVoiceBasic(int channel); - void setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan); - int calcVelocity(int voice, int op); -}; - -class MidiPlayer_AdLib : public MidiPlayer { -public: - MidiPlayer_AdLib() { _driver = new MidiDriver_AdLib(g_system->getMixer()); } - ~MidiPlayer_AdLib() { - delete _driver; - _driver = 0; - } - - int open(ResourceManager *resMan); - void close(); - - byte getPlayId(SciVersion soundVersion); - int getPolyphony() const { return MidiDriver_AdLib::kVoices; } - bool hasRhythmChannel() const { return false; } - void setVolume(byte volume) { static_cast(_driver)->setVolume(volume); } - void playSwitch(bool play) { static_cast(_driver)->playSwitch(play); } - void loadInstrument(int idx, byte *data); -}; - -static const byte registerOffset[MidiDriver_AdLib::kVoices] = { - 0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12 -}; - -static const byte velocityMap1[64] = { - 0x00, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x12, 0x13, - 0x14, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, - 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, - 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2d, 0x2d, 0x2e, - 0x2f, 0x30, 0x31, 0x32, 0x32, 0x33, 0x34, 0x34, - 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3a, - 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, - 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f -}; - -static const byte velocityMap2[64] = { - 0x00, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, - 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x21, - 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, - 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x2f, 0x30, - 0x31, 0x32, 0x32, 0x33, 0x34, 0x34, 0x35, 0x36, - 0x36, 0x37, 0x38, 0x38, 0x39, 0x39, 0x3a, 0x3a, - 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, - 0x3d, 0x3e, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f -}; - -static const int ym3812_note[13] = { - 0x157, 0x16b, 0x181, 0x198, 0x1b0, 0x1ca, - 0x1e5, 0x202, 0x220, 0x241, 0x263, 0x287, - 0x2ae -}; - -int MidiDriver_AdLib::open(bool isSCI0) { - int rate = _mixer->getOutputRate(); - - _stereo = STEREO; - - debug(3, "ADLIB: Starting driver in %s mode", (isSCI0 ? "SCI0" : "SCI1")); - _isSCI0 = isSCI0; - - _opl = OPL::Config::create(isStereo() ? OPL::Config::kDualOpl2 : OPL::Config::kOpl2); - - // Try falling back to mono, thus plain OPL2 emualtor, when no Dual OPL2 is available. - if (!_opl && _stereo) { - _stereo = false; - _opl = OPL::Config::create(OPL::Config::kOpl2); - } - - if (!_opl) - return -1; - - _opl->init(rate); - - setRegister(0xBD, 0); - setRegister(0x08, 0); - setRegister(0x01, 0x20); - - MidiDriver_Emulated::open(); - - _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); - - return 0; -} - -void MidiDriver_AdLib::close() { - _mixer->stopHandle(_mixerSoundHandle); - - delete _opl; - delete[] _rhythmKeyMap; -} - -void MidiDriver_AdLib::setVolume(byte volume) { - _masterVolume = volume; - renewNotes(-1, true); -} - -// MIDI messages can be found at http://www.midi.org/techspecs/midimessages.php -void MidiDriver_AdLib::send(uint32 b) { - byte command = b & 0xf0; - byte channel = b & 0xf; - byte op1 = (b >> 8) & 0xff; - byte op2 = (b >> 16) & 0xff; - - switch (command) { - case 0x80: - noteOff(channel, op1); - break; - case 0x90: - noteOn(channel, op1, op2); - break; - case 0xe0: - _channels[channel].pitchWheel = (op1 & 0x7f) | ((op2 & 0x7f) << 7); - renewNotes(channel, true); - break; - case 0xb0: - switch (op1) { - case 0x07: - _channels[channel].volume = op2 >> 1; - renewNotes(channel, true); - break; - case 0x0a: - _channels[channel].pan = op2; - renewNotes(channel, true); - break; - case 0x40: - _channels[channel].holdPedal = op2; - if (op2 == 0) { - for (int i = 0; i < kVoices; i++) { - if ((_voices[i].channel == channel) && _voices[i].isSustained) - voiceOff(i); - } - } - break; - case 0x4b: -#ifndef ADLIB_DISABLE_VOICE_MAPPING - voiceMapping(channel, op2); -#endif - break; - case 0x4e: - _channels[channel].enableVelocity = op2; - break; - case SCI_MIDI_CHANNEL_NOTES_OFF: - for (int i = 0; i < kVoices; i++) - if ((_voices[i].channel == channel) && (_voices[i].note != -1)) - voiceOff(i); - break; - default: - //warning("ADLIB: ignoring MIDI command %02x %02x %02x", command | channel, op1, op2); - break; - } - break; - case 0xc0: - _channels[channel].patch = op1; - break; - // The original adlib driver from sierra ignores aftertouch completely, so should we - case 0xa0: // Polyphonic key pressure (aftertouch) - case 0xd0: // Channel pressure (aftertouch) - break; - case 0xf0: // SysEx, ignore it - break; - default: - warning("ADLIB: Unknown event %02x", command); - } -} - -void MidiDriver_AdLib::generateSamples(int16 *data, int len) { - if (isStereo()) - len <<= 1; - _opl->readBuffer(data, len); - - // Increase the age of the notes - for (int i = 0; i < kVoices; i++) { - if (_voices[i].note != -1) - _voices[i].age++; - } -} - -void MidiDriver_AdLib::loadInstrument(const byte *ins) { - AdLibPatch patch; - - // Set data for the operators - for (int i = 0; i < 2; i++) { - const byte *op = ins + i * 13; - patch.op[i].kbScaleLevel = op[0] & 0x3; - patch.op[i].frequencyMult = op[1] & 0xf; - patch.op[i].attackRate = op[3] & 0xf; - patch.op[i].sustainLevel = op[4] & 0xf; - patch.op[i].envelopeType = op[5]; - patch.op[i].decayRate = op[6] & 0xf; - patch.op[i].releaseRate = op[7] & 0xf; - patch.op[i].totalLevel = op[8] & 0x3f; - patch.op[i].amplitudeMod = op[9]; - patch.op[i].vibrato = op[10]; - patch.op[i].kbScaleRate = op[11]; - } - patch.op[0].waveForm = ins[26] & 0x3; - patch.op[1].waveForm = ins[27] & 0x3; - - // Set data for the modulator - patch.mod.feedback = ins[2] & 0x7; - patch.mod.algorithm = !ins[12]; // Flag is inverted - - _patches.push_back(patch); -} - -void MidiDriver_AdLib::voiceMapping(int channel, int voices) { - int curVoices = 0; - - for (int i = 0; i < kVoices; i++) - if (_voices[i].channel == channel) - curVoices++; - - curVoices += _channels[channel].extraVoices; - - if (curVoices < voices) { - debug(3, "ADLIB: assigning %i additional voices to channel %i", voices - curVoices, channel); - assignVoices(channel, voices - curVoices); - } else if (curVoices > voices) { - debug(3, "ADLIB: releasing %i voices from channel %i", curVoices - voices, channel); - releaseVoices(channel, curVoices - voices); - donateVoices(); - } -} - -void MidiDriver_AdLib::assignVoices(int channel, int voices) { - assert(voices > 0); - - for (int i = 0; i < kVoices; i++) - if (_voices[i].channel == -1) { - _voices[i].channel = channel; - if (--voices == 0) - return; - } - - _channels[channel].extraVoices += voices; -} - -void MidiDriver_AdLib::releaseVoices(int channel, int voices) { - if (_channels[channel].extraVoices >= voices) { - _channels[channel].extraVoices -= voices; - return; - } - - voices -= _channels[channel].extraVoices; - _channels[channel].extraVoices = 0; - - for (int i = 0; i < kVoices; i++) { - if ((_voices[i].channel == channel) && (_voices[i].note == -1)) { - _voices[i].channel = -1; - if (--voices == 0) - return; - } - } - - for (int i = 0; i < kVoices; i++) { - if (_voices[i].channel == channel) { - voiceOff(i); - _voices[i].channel = -1; - if (--voices == 0) - return; - } - } -} - -void MidiDriver_AdLib::donateVoices() { - int freeVoices = 0; - - for (int i = 0; i < kVoices; i++) - if (_voices[i].channel == -1) - freeVoices++; - - if (freeVoices == 0) - return; - - for (int i = 0; i < MIDI_CHANNELS; i++) { - if (_channels[i].extraVoices >= freeVoices) { - assignVoices(i, freeVoices); - _channels[i].extraVoices -= freeVoices; - return; - } else if (_channels[i].extraVoices > 0) { - assignVoices(i, _channels[i].extraVoices); - freeVoices -= _channels[i].extraVoices; - _channels[i].extraVoices = 0; - } - } -} - -void MidiDriver_AdLib::renewNotes(int channel, bool key) { - for (int i = 0; i < kVoices; i++) { - // Update all notes playing this channel - if ((channel == -1) || (_voices[i].channel == channel)) { - if (_voices[i].note != -1) - setNote(i, _voices[i].note, key); - } - } -} - -void MidiDriver_AdLib::noteOn(int channel, int note, int velocity) { - if (velocity == 0) - return noteOff(channel, note); - - velocity >>= 1; - - // Check for playable notes - if ((note < 12) || (note > 107)) - return; - - for (int i = 0; i < kVoices; i++) { - if ((_voices[i].channel == channel) && (_voices[i].note == note)) { - voiceOff(i); - voiceOn(i, note, velocity); - return; - } - } - -#ifdef ADLIB_DISABLE_VOICE_MAPPING - int voice = findVoiceBasic(channel); -#else - int voice = findVoice(channel); -#endif - - if (voice == -1) { - debug(3, "ADLIB: failed to find free voice assigned to channel %i", channel); - return; - } - - voiceOn(voice, note, velocity); -} - -// FIXME: Temporary, see comment at top of file regarding ADLIB_DISABLE_VOICE_MAPPING -int MidiDriver_AdLib::findVoiceBasic(int channel) { - int voice = -1; - int oldestVoice = -1; - int oldestAge = -1; - - // Try to find a voice assigned to this channel that is free (round-robin) - for (int i = 0; i < kVoices; i++) { - int v = (_channels[channel].lastVoice + i + 1) % kVoices; - - if (_voices[v].note == -1) { - voice = v; - break; - } - - // We also keep track of the oldest note in case the search fails - if (_voices[v].age > oldestAge) { - oldestAge = _voices[v].age; - oldestVoice = v; - } - } - - if (voice == -1) { - if (oldestVoice != -1) { - voiceOff(oldestVoice); - voice = oldestVoice; - } else { - return -1; - } - } - - _voices[voice].channel = channel; - _channels[channel].lastVoice = voice; - return voice; -} - -int MidiDriver_AdLib::findVoice(int channel) { - int voice = -1; - int oldestVoice = -1; - uint32 oldestAge = 0; - - // Try to find a voice assigned to this channel that is free (round-robin) - for (int i = 0; i < kVoices; i++) { - int v = (_channels[channel].lastVoice + i + 1) % kVoices; - - if (_voices[v].channel == channel) { - if (_voices[v].note == -1) { - voice = v; - break; - } - - // We also keep track of the oldest note in case the search fails - // Notes started in the current time slice will not be selected - if (_voices[v].age > oldestAge) { - oldestAge = _voices[v].age; - oldestVoice = v; - } - } - } - - if (voice == -1) { - if (oldestVoice != -1) { - voiceOff(oldestVoice); - voice = oldestVoice; - } else { - return -1; - } - } - - _channels[channel].lastVoice = voice; - return voice; -} - -void MidiDriver_AdLib::noteOff(int channel, int note) { - for (int i = 0; i < kVoices; i++) { - if ((_voices[i].channel == channel) && (_voices[i].note == note)) { - if (_channels[channel].holdPedal) - _voices[i].isSustained = true; - else - voiceOff(i); - return; - } - } -} - -void MidiDriver_AdLib::voiceOn(int voice, int note, int velocity) { - int channel = _voices[voice].channel; - int patch; - - _voices[voice].age = 0; - - if ((channel == 9) && _rhythmKeyMap) { - patch = CLIP(note, 27, 88) + 101; - } else { - patch = _channels[channel].patch; - } - - // Set patch if different from current patch - if ((patch != _voices[voice].patch) && _playSwitch) - setPatch(voice, patch); - - _voices[voice].velocity = velocity; - setNote(voice, note, true); -} - -void MidiDriver_AdLib::voiceOff(int voice) { - _voices[voice].isSustained = false; - setNote(voice, _voices[voice].note, 0); - _voices[voice].note = -1; - _voices[voice].age = 0; -} - -void MidiDriver_AdLib::setNote(int voice, int note, bool key) { - int channel = _voices[voice].channel; - int n, fre, oct; - float delta; - int bend = _channels[channel].pitchWheel; - - if ((channel == 9) && _rhythmKeyMap) { - note = _rhythmKeyMap[CLIP(note, 27, 88) - 27]; - } - - _voices[voice].note = note; - - delta = 0; - - n = note % 12; - - if (bend < 8192) - bend = 8192 - bend; - delta = (float)pow(2.0, (bend % 8192) / 8192.0); - - if (bend > 8192) - fre = (int)(ym3812_note[n] * delta); - else - fre = (int)(ym3812_note[n] / delta); - - oct = note / 12 - 1; - - if (oct < 0) - oct = 0; - - if (oct > 7) - oct = 7; - - setRegister(0xA0 + voice, fre & 0xff); - setRegister(0xB0 + voice, (key << 5) | (oct << 2) | (fre >> 8)); - - setVelocity(voice); -} - -void MidiDriver_AdLib::setVelocity(int voice) { - AdLibPatch &patch = _patches[_voices[voice].patch]; - int pan = _channels[_voices[voice].channel].pan; - setVelocityReg(registerOffset[voice] + 3, calcVelocity(voice, 1), patch.op[1].kbScaleLevel, pan); - - // In AM mode we need to set the level for both operators - if (_patches[_voices[voice].patch].mod.algorithm == 1) - setVelocityReg(registerOffset[voice], calcVelocity(voice, 0), patch.op[0].kbScaleLevel, pan); -} - -int MidiDriver_AdLib::calcVelocity(int voice, int op) { - if (_isSCI0) { - int velocity = _masterVolume; - - if (velocity > 0) - velocity += 3; - - if (velocity > 15) - velocity = 15; - - int insVelocity; - if (_channels[_voices[voice].channel].enableVelocity) - insVelocity = _voices[voice].velocity; - else - insVelocity = 63 - _patches[_voices[voice].patch].op[op].totalLevel; - - // Note: Later SCI0 has a static table that is close to this formula, but not exactly the same. - // Early SCI0 does (velocity * (insVelocity / 15)) - return velocity * insVelocity / 15; - } else { - AdLibOperator &oper = _patches[_voices[voice].patch].op[op]; - int velocity = _channels[_voices[voice].channel].volume + 1; - velocity = velocity * (velocityMap1[_voices[voice].velocity] + 1) / 64; - velocity = velocity * (_masterVolume + 1) / 16; - - if (--velocity < 0) - velocity = 0; - - return velocityMap2[velocity] * (63 - oper.totalLevel) / 63; - } -} - -void MidiDriver_AdLib::setVelocityReg(int regOffset, int velocity, int kbScaleLevel, int pan) { - if (!_playSwitch) - velocity = 0; - - if (isStereo()) { - int velLeft = velocity; - int velRight = velocity; - - if (pan > 0x40) - velLeft = velLeft * (0x7f - pan) / 0x3f; - else if (pan < 0x40) - velRight = velRight * pan / 0x40; - - setRegister(0x40 + regOffset, (kbScaleLevel << 6) | (63 - velLeft), kLeftChannel); - setRegister(0x40 + regOffset, (kbScaleLevel << 6) | (63 - velRight), kRightChannel); - } else { - setRegister(0x40 + regOffset, (kbScaleLevel << 6) | (63 - velocity)); - } -} - -void MidiDriver_AdLib::setPatch(int voice, int patch) { - if ((patch < 0) || ((uint)patch >= _patches.size())) { - warning("ADLIB: Invalid patch %i requested", patch); - patch = 0; - } - - _voices[voice].patch = patch; - AdLibModulator &mod = _patches[patch].mod; - - // Set the common settings for both operators - setOperator(registerOffset[voice], _patches[patch].op[0]); - setOperator(registerOffset[voice] + 3, _patches[patch].op[1]); - - // Set the additional settings for the modulator - byte algorithm = mod.algorithm ? 1 : 0; - setRegister(0xC0 + voice, (mod.feedback << 1) | algorithm); -} - -void MidiDriver_AdLib::setOperator(int reg, AdLibOperator &op) { - setRegister(0x40 + reg, (op.kbScaleLevel << 6) | op.totalLevel); - setRegister(0x60 + reg, (op.attackRate << 4) | op.decayRate); - setRegister(0x80 + reg, (op.sustainLevel << 4) | op.releaseRate); - setRegister(0x20 + reg, (op.amplitudeMod << 7) | (op.vibrato << 6) - | (op.envelopeType << 5) | (op.kbScaleRate << 4) | op.frequencyMult); - setRegister(0xE0 + reg, op.waveForm); -} - -void MidiDriver_AdLib::setRegister(int reg, int value, int channels) { - if (channels & kLeftChannel) { - _opl->write(0x220, reg); - _opl->write(0x221, value); - } - - if (isStereo()) { - if (channels & kRightChannel) { - _opl->write(0x222, reg); - _opl->write(0x223, value); - } - } -} - -void MidiDriver_AdLib::playSwitch(bool play) { - _playSwitch = play; - renewNotes(-1, play); -} - -bool MidiDriver_AdLib::loadResource(const byte *data, uint size) { - if ((size != 1344) && (size != 2690) && (size != 5382)) { - warning("ADLIB: Unsupported patch format (%i bytes)", size); - return false; - } - - for (int i = 0; i < 48; i++) - loadInstrument(data + (28 * i)); - - if (size == 2690) { - for (int i = 48; i < 96; i++) - loadInstrument(data + 2 + (28 * i)); - } else if (size == 5382) { - for (int i = 48; i < 190; i++) - loadInstrument(data + (28 * i)); - _rhythmKeyMap = new byte[kRhythmKeys]; - memcpy(_rhythmKeyMap, data + 5320, kRhythmKeys); - } - - return true; -} - -uint32 MidiDriver_AdLib::property(int prop, uint32 param) { - switch(prop) { - case MIDI_PROP_MASTER_VOLUME: - if (param != 0xffff) - _masterVolume = param; - return _masterVolume; - default: - break; - } - return 0; -} - - -int MidiPlayer_AdLib::open(ResourceManager *resMan) { - assert(resMan != NULL); - - // Load up the patch.003 file, parse out the instruments - Resource *res = resMan->findResource(ResourceId(kResourceTypePatch, 3), 0); - bool ok = false; - - if (res) { - ok = static_cast(_driver)->loadResource(res->data, res->size); - } else { - // Early SCI0 games have the sound bank embedded in the adlib driver - - Common::File f; - - if (f.open("ADL.DRV")) { - int size = f.size(); - const uint patchSize = 1344; - - if ((size == 5684) || (size == 5720) || (size == 5727)) { - byte *buf = new byte[patchSize]; - - if (f.seek(0x45a) && (f.read(buf, patchSize) == patchSize)) - ok = static_cast(_driver)->loadResource(buf, patchSize); - - delete[] buf; - } - } - } - - if (!ok) { - warning("ADLIB: Failed to load patch.003"); - return -1; - } - - return static_cast(_driver)->open(getSciVersion() <= SCI_VERSION_0_LATE); -} - -void MidiPlayer_AdLib::close() { - if (_driver) { - _driver->close(); - } -} - -byte MidiPlayer_AdLib::getPlayId(SciVersion soundVersion) { - switch (soundVersion) { - case SCI_VERSION_0_EARLY: - return 0x01; - case SCI_VERSION_0_LATE: - return 0x04; - default: - return 0x00; - } -} - -MidiPlayer *MidiPlayer_AdLib_create() { - return new MidiPlayer_AdLib(); -} - -} // End of namespace Sci diff --git a/engines/sci/sound/softseq/amiga.cpp b/engines/sci/sound/softseq/amiga.cpp deleted file mode 100644 index 7c57bb6efd..0000000000 --- a/engines/sci/sound/softseq/amiga.cpp +++ /dev/null @@ -1,676 +0,0 @@ -/* 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. - * - * $URL$ - * $Id$ - * - */ - -#include "sound/softsynth/emumidi.h" -#include "sci/sound/softseq/mididriver.h" - -#include "common/file.h" -#include "common/frac.h" -#include "common/util.h" - -namespace Sci { - -/* #define DEBUG */ - -// Frequencies for every note -// FIXME Store only one octave -static const int freq_table[] = { - 58, 62, 65, 69, 73, 78, 82, 87, - 92, 98, 104, 110, 117, 124, 131, 139, - 147, 156, 165, 175, 185, 196, 208, 220, - 234, 248, 262, 278, 294, 312, 331, 350, - 371, 393, 417, 441, 468, 496, 525, 556, - 589, 625, 662, 701, 743, 787, 834, 883, - 936, 992, 1051, 1113, 1179, 1250, 1324, 1403, - 1486, 1574, 1668, 1767, 1872, 1984, 2102, 2227, - 2359, 2500, 2648, 2806, 2973, 3149, 3337, 3535, - 3745, 3968, 4204, 4454, 4719, 5000, 5297, 5612, - 5946, 6299, 6674, 7071, 7491, 7937, 8408, 8908, - 9438, 10000, 10594, 11224, 11892, 12599, 13348, 14142, - 14983, 15874, 16817, 17817, 18877, 20000, 21189, 22449, - 23784, 25198, 26696, 28284, 29966, 31748, 33635, 35635, - 37754, 40000, 42378, 44898, 47568, 50396, 53393, 56568, - 59932, 63496, 67271, 71271, 75509, 80000, 84757, 89796 -}; - -class MidiDriver_Amiga : public MidiDriver_Emulated { -public: - enum { - kVoices = 4 - }; - - MidiDriver_Amiga(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer), _playSwitch(true), _masterVolume(15) { } - virtual ~MidiDriver_Amiga() { } - - // MidiDriver - int open(); - void close(); - void send(uint32 b); - MidiChannel *allocateChannel() { return NULL; } - MidiChannel *getPercussionChannel() { return NULL; } - - // AudioStream - bool isStereo() const { return true; } - int getRate() const { return _mixer->getOutputRate(); } - - // MidiDriver_Emulated - void generateSamples(int16 *buf, int len); - - void setVolume(byte volume); - void playSwitch(bool play); - virtual uint32 property(int prop, uint32 param); - -private: - enum { - kModeLoop = 1 << 0, // Instrument looping flag - kModePitch = 1 << 1 // Instrument pitch changes flag - }; - - enum { - kChannels = 10, - kBaseFreq = 20000, // Samplerate of the instrument bank - kPanLeft = 91, - kPanRight = 164 - }; - - struct Channel { - int instrument; - int volume; - int pan; - }; - - struct Envelope { - int length; // Phase period length in samples - int delta; // Velocity delta per period - int target; // Target velocity - }; - - struct Voice { - int instrument; - int note; - int note_velocity; - int velocity; - int envelope; - int envelope_samples; // Number of samples till next envelope event - int decay; - int looping; - int hw_channel; - frac_t offset; - frac_t rate; - }; - - struct Instrument { - char name[30]; - int mode; - int size; // Size of non-looping part in bytes - int loop_size; // Starting offset and size of loop in bytes - int transpose; // Transpose value in semitones - Envelope envelope[4]; // Envelope - int8 *samples; - int8 *loop; - }; - - struct Bank { - char name[30]; - uint size; - Instrument *instruments[256]; - }; - - bool _playSwitch; - int _masterVolume; - int _frequency; - Envelope _envDecay; - Bank _bank; // Instrument bank - - Channel _channels[MIDI_CHANNELS]; - /* Internal channels */ - Voice _voices[kChannels]; - - void setEnvelope(Voice *channel, Envelope *envelope, int phase); - int interpolate(int8 *samples, frac_t offset); - void playInstrument(int16 *dest, Voice *channel, int count); - void changeInstrument(int channel, int instrument); - void stopChannel(int ch); - void stopNote(int ch, int note); - void startNote(int ch, int note, int velocity); - Instrument *readInstrument(Common::File &file, int *id); -}; - -void MidiDriver_Amiga::setEnvelope(Voice *channel, Envelope *envelope, int phase) { - channel->envelope = phase; - channel->envelope_samples = envelope[phase].length; - - if (phase == 0) - channel->velocity = channel->note_velocity / 2; - else - channel->velocity = envelope[phase - 1].target; -} - -int MidiDriver_Amiga::interpolate(int8 *samples, frac_t offset) { - int x = fracToInt(offset); - int diff = (samples[x + 1] - samples[x]) << 8; - - return (samples[x] << 8) + fracToInt(diff * (offset & FRAC_LO_MASK)); -} - -void MidiDriver_Amiga::playInstrument(int16 *dest, Voice *channel, int count) { - int index = 0; - int vol = _channels[channel->hw_channel].volume; - Instrument *instrument = _bank.instruments[channel->instrument]; - - while (1) { - /* Available source samples until end of segment */ - frac_t lin_avail; - int seg_end, rem, i, amount; - int8 *samples; - - if (channel->looping) { - samples = instrument->loop; - seg_end = instrument->loop_size; - } else { - samples = instrument->samples; - seg_end = instrument->size; - } - - lin_avail = intToFrac(seg_end) - channel->offset; - - rem = count - index; - - /* Amount of destination samples that we will compute this iteration */ - amount = lin_avail / channel->rate; - - if (lin_avail % channel->rate) - amount++; - - if (amount > rem) - amount = rem; - - /* Stop at next envelope event */ - if ((channel->envelope_samples != -1) && (amount > channel->envelope_samples)) - amount = channel->envelope_samples; - - for (i = 0; i < amount; i++) { - dest[index++] = interpolate(samples, channel->offset) * channel->velocity / 64 * channel->note_velocity * vol / (127 * 127); - channel->offset += channel->rate; - } - - if (channel->envelope_samples != -1) - channel->envelope_samples -= amount; - - if (channel->envelope_samples == 0) { - Envelope *envelope; - int delta, target, velocity; - - if (channel->decay) - envelope = &_envDecay; - else - envelope = &instrument->envelope[channel->envelope]; - - delta = envelope->delta; - target = envelope->target; - velocity = channel->velocity - envelope->delta; - - /* Check whether we have reached the velocity target for the current phase */ - if ((delta >= 0 && velocity <= target) || (delta < 0 && velocity >= target)) { - channel->velocity = target; - - /* Stop note after velocity has dropped to 0 */ - if (target == 0) { - channel->note = -1; - break; - } else - switch (channel->envelope) { - case 0: - case 2: - /* Go to next phase */ - setEnvelope(channel, instrument->envelope, channel->envelope + 1); - break; - case 1: - case 3: - /* Stop envelope */ - channel->envelope_samples = -1; - break; - } - } else { - /* We haven't reached the target yet */ - channel->envelope_samples = envelope->length; - channel->velocity = velocity; - } - } - - if (index == count) - break; - - if (fracToInt(channel->offset) >= seg_end) { - if (instrument->mode & kModeLoop) { - /* Loop the samples */ - channel->offset -= intToFrac(seg_end); - channel->looping = 1; - } else { - /* All samples have been played */ - channel->note = -1; - break; - } - } - } -} - -void MidiDriver_Amiga::changeInstrument(int channel, int instrument) { -#ifdef DEBUG - if (_bank.instruments[instrument]) - printf("[sfx:seq:amiga] Setting channel %i to \"%s\" (%i)\n", channel, _bank.instruments[instrument]->name, instrument); - else - warning("[sfx:seq:amiga] instrument %i does not exist (channel %i)", instrument, channel); -#endif - _channels[channel].instrument = instrument; -} - -void MidiDriver_Amiga::stopChannel(int ch) { - int i; - - /* Start decay phase for note on this hw channel, if any */ - for (i = 0; i < kChannels; i++) - if (_voices[i].note != -1 && _voices[i].hw_channel == ch && !_voices[i].decay) { - /* Trigger fast decay envelope */ - _voices[i].decay = 1; - _voices[i].envelope_samples = _envDecay.length; - break; - } -} - -void MidiDriver_Amiga::stopNote(int ch, int note) { - int channel; - Instrument *instrument; - - for (channel = 0; channel < kChannels; channel++) - if (_voices[channel].note == note && _voices[channel].hw_channel == ch && !_voices[channel].decay) - break; - - if (channel == kChannels) { -#ifdef DEBUG - warning("[sfx:seq:amiga] cannot stop note %i on channel %i", note, ch); -#endif - return; - } - - instrument = _bank.instruments[_voices[channel].instrument]; - - /* Start the envelope phases for note-off if looping is on and envelope is enabled */ - if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0)) - setEnvelope(&_voices[channel], instrument->envelope, 2); -} - -void MidiDriver_Amiga::startNote(int ch, int note, int velocity) { - Instrument *instrument; - int channel; - - if (_channels[ch].instrument < 0 || _channels[ch].instrument > 255) { - warning("[sfx:seq:amiga] invalid instrument %i on channel %i", _channels[ch].instrument, ch); - return; - } - - instrument = _bank.instruments[_channels[ch].instrument]; - - if (!instrument) { - warning("[sfx:seq:amiga] instrument %i does not exist", _channels[ch].instrument); - return; - } - - for (channel = 0; channel < kChannels; channel++) - if (_voices[channel].note == -1) - break; - - if (channel == kChannels) { - warning("[sfx:seq:amiga] could not find a free channel"); - return; - } - - stopChannel(ch); - - if (instrument->mode & kModePitch) { - int fnote = note + instrument->transpose; - - if (fnote < 0 || fnote > 127) { - warning("[sfx:seq:amiga] illegal note %i\n", fnote); - return; - } - - /* Compute rate for note */ - _voices[channel].rate = doubleToFrac(freq_table[fnote] / (double) _frequency); - } else - _voices[channel].rate = doubleToFrac(kBaseFreq / (double) _frequency); - - _voices[channel].instrument = _channels[ch].instrument; - _voices[channel].note = note; - _voices[channel].note_velocity = velocity; - - if ((instrument->mode & kModeLoop) && (instrument->envelope[0].length != 0)) - setEnvelope(&_voices[channel], instrument->envelope, 0); - else { - _voices[channel].velocity = 64; - _voices[channel].envelope_samples = -1; - } - - _voices[channel].offset = 0; - _voices[channel].hw_channel = ch; - _voices[channel].decay = 0; - _voices[channel].looping = 0; -} - -MidiDriver_Amiga::Instrument *MidiDriver_Amiga::readInstrument(Common::File &file, int *id) { - Instrument *instrument; - byte header[61]; - int size; - int seg_size[3]; - int loop_offset; - int i; - - if (file.read(header, 61) < 61) { - warning("[sfx:seq:amiga] failed to read instrument header"); - return NULL; - } - - instrument = new Instrument; - - seg_size[0] = READ_BE_UINT16(header + 35) * 2; - seg_size[1] = READ_BE_UINT16(header + 41) * 2; - seg_size[2] = READ_BE_UINT16(header + 47) * 2; - - instrument->mode = header[33]; - instrument->transpose = (int8) header[34]; - for (i = 0; i < 4; i++) { - int length = (int8) header[49 + i]; - - if (length == 0 && i > 0) - length = 256; - - instrument->envelope[i].length = length * _frequency / 60; - instrument->envelope[i].delta = (int8)header[53 + i]; - instrument->envelope[i].target = header[57 + i]; - } - /* Final target must be 0 */ - instrument->envelope[3].target = 0; - - loop_offset = READ_BE_UINT32(header + 37) & ~1; - size = seg_size[0] + seg_size[1] + seg_size[2]; - - *id = READ_BE_UINT16(header); - - strncpy(instrument->name, (char *) header + 2, 29); - instrument->name[29] = 0; -#ifdef DEBUG - printf("[sfx:seq:amiga] Reading instrument %i: \"%s\" (%i bytes)\n", - *id, instrument->name, size); - printf(" Mode: %02x\n", instrument->mode); - printf(" Looping: %s\n", instrument->mode & kModeLoop ? "on" : "off"); - printf(" Pitch changes: %s\n", instrument->mode & kModePitch ? "on" : "off"); - printf(" Segment sizes: %i %i %i\n", seg_size[0], seg_size[1], seg_size[2]); - printf(" Segment offsets: 0 %i %i\n", loop_offset, read_int32(header + 43)); -#endif - instrument->samples = (int8 *) malloc(size + 1); - if (file.read(instrument->samples, size) < (unsigned int)size) { - warning("[sfx:seq:amiga] failed to read instrument samples"); - free(instrument->samples); - delete instrument; - return NULL; - } - - if (instrument->mode & kModeLoop) { - if (loop_offset + seg_size[1] > size) { -#ifdef DEBUG - warning("[sfx:seq:amiga] looping samples extend %i bytes past end of sample block", - loop_offset + seg_size[1] - size); -#endif - seg_size[1] = size - loop_offset; - } - - if (seg_size[1] < 0) { - warning("[sfx:seq:amiga] invalid looping point"); - free(instrument->samples); - delete instrument; - return NULL; - } - - instrument->size = seg_size[0]; - instrument->loop_size = seg_size[1]; - - instrument->loop = (int8*)malloc(instrument->loop_size + 1); - memcpy(instrument->loop, instrument->samples + loop_offset, instrument->loop_size); - - instrument->samples[instrument->size] = instrument->loop[0]; - instrument->loop[instrument->loop_size] = instrument->loop[0]; - } else { - instrument->loop = NULL; - instrument->size = size; - instrument->samples[instrument->size] = 0; - } - - return instrument; -} - -uint32 MidiDriver_Amiga::property(int prop, uint32 param) { - switch(prop) { - case MIDI_PROP_MASTER_VOLUME: - if (param != 0xffff) - _masterVolume = param; - return _masterVolume; - default: - break; - } - return 0; -} - -int MidiDriver_Amiga::open() { - _frequency = _mixer->getOutputRate(); - _envDecay.length = _frequency / (32 * 64); - _envDecay.delta = 1; - _envDecay.target = 0; - - Common::File file; - byte header[40]; - - if (!file.open("bank.001")) { - warning("[sfx:seq:amiga] file bank.001 not found"); - return Common::kUnknownError; - } - - if (file.read(header, 40) < 40) { - warning("[sfx:seq:amiga] failed to read header of file bank.001"); - return Common::kUnknownError; - } - - for (uint i = 0; i < 256; i++) - _bank.instruments[i] = NULL; - - for (uint i = 0; i < kChannels; i++) { - _voices[i].note = -1; - _voices[i].hw_channel = 0; - } - - for (uint i = 0; i < MIDI_CHANNELS; i++) { - _channels[i].instrument = -1; - _channels[i].volume = 127; - _channels[i].pan = (i % 4 == 0 || i % 4 == 3 ? kPanLeft : kPanRight); - } - - _bank.size = READ_BE_UINT16(header + 38); - strncpy(_bank.name, (char *) header + 8, 29); - _bank.name[29] = 0; -#ifdef DEBUG - printf("[sfx:seq:amiga] Reading %i instruments from bank \"%s\"\n", _bank.size, _bank.name); -#endif - - for (uint i = 0; i < _bank.size; i++) { - int id; - Instrument *instrument = readInstrument(file, &id); - - if (!instrument) { - warning("[sfx:seq:amiga] failed to read bank.001"); - return Common::kUnknownError; - } - - if (id < 0 || id > 255) { - warning("[sfx:seq:amiga] Error: instrument ID out of bounds"); - return Common::kUnknownError; - } - - _bank.instruments[id] = instrument; - } - - MidiDriver_Emulated::open(); - - _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); - - return Common::kNoError; -} - -void MidiDriver_Amiga::close() { - _mixer->stopHandle(_mixerSoundHandle); - - for (uint i = 0; i < _bank.size; i++) { - if (_bank.instruments[i]) { - if (_bank.instruments[i]->loop) - free(_bank.instruments[i]->loop); - free(_bank.instruments[i]->samples); - delete _bank.instruments[i]; - } - } -} - -void MidiDriver_Amiga::playSwitch(bool play) { - _playSwitch = play; -} - -void MidiDriver_Amiga::setVolume(byte volume_) { - _masterVolume = volume_; -} - -void MidiDriver_Amiga::send(uint32 b) { - byte command = b & 0xf0; - byte channel = b & 0xf; - byte op1 = (b >> 8) & 0xff; - byte op2 = (b >> 16) & 0xff; - - switch (command) { - case 0x80: - stopNote(channel, op1); - break; - case 0x90: - if (op2 > 0) - startNote(channel, op1, op2); - else - stopNote(channel, op1); - break; - case 0xb0: - switch (op1) { - case 0x07: - _channels[channel].volume = op2; - break; - case 0x0a: -#ifdef DEBUG - warning("[sfx:seq:amiga] ignoring pan 0x%02x event for channel %i", op2, channel); -#endif - break; - case 0x7b: - stopChannel(channel); - break; - default: - warning("[sfx:seq:amiga] unknown control event 0x%02x", op1); - } - break; - case 0xc0: - changeInstrument(channel, op1); - break; - default: - warning("[sfx:seq:amiga] unknown event %02x", command); - } -} - -void MidiDriver_Amiga::generateSamples(int16 *data, int len) { - if (len == 0) - return; - - int16 *buffers = (int16*)malloc(len * 2 * kChannels); - - memset(buffers, 0, len * 2 * kChannels); - - /* Generate samples for all notes */ - for (int i = 0; i < kChannels; i++) - if (_voices[i].note >= 0) - playInstrument(buffers + i * len, &_voices[i], len); - - if (isStereo()) { - for (int j = 0; j < len; j++) { - int mixedl = 0, mixedr = 0; - - /* Mix and pan */ - for (int i = 0; i < kChannels; i++) { - mixedl += buffers[i * len + j] * (256 - _channels[_voices[i].hw_channel].pan); - mixedr += buffers[i * len + j] * _channels[_voices[i].hw_channel].pan; - } - - /* Adjust volume */ - data[2 * j] = mixedl * _masterVolume >> 13; - data[2 * j + 1] = mixedr * _masterVolume >> 13; - } - } else { - for (int j = 0; j < len; j++) { - int mixed = 0; - - /* Mix */ - for (int i = 0; i < kChannels; i++) - mixed += buffers[i * len + j]; - - /* Adjust volume */ - data[j] = mixed * _masterVolume >> 6; - } - } - - free(buffers); -} - -class MidiPlayer_Amiga : public MidiPlayer { -public: - MidiPlayer_Amiga() { _driver = new MidiDriver_Amiga(g_system->getMixer()); } - byte getPlayId(SciVersion soundVersion); - int getPolyphony() const { return MidiDriver_Amiga::kVoices; } - bool hasRhythmChannel() const { return false; } - void setVolume(byte volume) { static_cast(_driver)->setVolume(volume); } - void playSwitch(bool play) { static_cast(_driver)->playSwitch(play); } - void loadInstrument(int idx, byte *data); -}; - -MidiPlayer *MidiPlayer_Amiga_create() { - return new MidiPlayer_Amiga(); -} - -byte MidiPlayer_Amiga::getPlayId(SciVersion soundVersion) { - if (soundVersion != SCI_VERSION_0_LATE) - error("Amiga sound support not available for this SCI version"); - - return 0x40; -} - -} // End of namespace Sci diff --git a/engines/sci/sound/softseq/map-mt32-to-gm.h b/engines/sci/sound/softseq/map-mt32-to-gm.h deleted file mode 100644 index d87ab777d7..0000000000 --- a/engines/sci/sound/softseq/map-mt32-to-gm.h +++ /dev/null @@ -1,552 +0,0 @@ -/* 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. - * - * $URL$ - * $Id$ - * - */ - -namespace Sci { - -/* Patch not mapped */ -#define MIDI_UNMAPPED 0xff -/* Patch mapped to rhythm key */ -#define MIDI_MAPPED_TO_RHYTHM 0xfe - -struct Mt32ToGmMap { - const char *name; - uint8 gmInstr; - uint8 gmRhythmKey; -}; - -static const char *GmInstrumentNames[] = { - /*000*/ "Acoustic Grand Piano", - /*001*/ "Bright Acoustic Piano", - /*002*/ "Electric Grand Piano", - /*003*/ "Honky-tonk Piano", - /*004*/ "Electric Piano 1", - /*005*/ "Electric Piano 2", - /*006*/ "Harpsichord", - /*007*/ "Clavinet", - /*008*/ "Celesta", - /*009*/ "Glockenspiel", - /*010*/ "Music Box", - /*011*/ "Vibraphone", - /*012*/ "Marimba", - /*013*/ "Xylophone", - /*014*/ "Tubular Bells", - /*015*/ "Dulcimer", - /*016*/ "Drawbar Organ", - /*017*/ "Percussive Organ", - /*018*/ "Rock Organ", - /*019*/ "Church Organ", - /*020*/ "Reed Organ", - /*021*/ "Accordion", - /*022*/ "Harmonica", - /*023*/ "Tango Accordion", - /*024*/ "Acoustic Guitar (nylon)", - /*025*/ "Acoustic Guitar (steel)", - /*026*/ "Electric Guitar (jazz)", - /*027*/ "Electric Guitar (clean)", - /*028*/ "Electric Guitar (muted)", - /*029*/ "Overdriven Guitar", - /*030*/ "Distortion Guitar", - /*031*/ "Guitar Harmonics", - /*032*/ "Acoustic Bass", - /*033*/ "Electric Bass (finger)", - /*034*/ "Electric Bass (pick)", - /*035*/ "Fretless Bass", - /*036*/ "Slap Bass 1", - /*037*/ "Slap Bass 2", - /*038*/ "Synth Bass 1", - /*039*/ "Synth Bass 2", - /*040*/ "Violin", - /*041*/ "Viola", - /*042*/ "Cello", - /*043*/ "Contrabass", - /*044*/ "Tremolo Strings", - /*045*/ "Pizzicato Strings", - /*046*/ "Orchestral Harp", - /*047*/ "Timpani", - /*048*/ "String Ensemble 1", - /*049*/ "String Ensemble 2", - /*050*/ "SynthStrings 1", - /*051*/ "SynthStrings 2", - /*052*/ "Choir Aahs", - /*053*/ "Voice Oohs", - /*054*/ "Synth Voice", - /*055*/ "Orchestra Hit", - /*056*/ "Trumpet", - /*057*/ "Trombone", - /*058*/ "Tuba", - /*059*/ "Muted Trumpet", - /*060*/ "French Horn", - /*061*/ "Brass Section", - /*062*/ "SynthBrass 1", - /*063*/ "SynthBrass 2", - /*064*/ "Soprano Sax", - /*065*/ "Alto Sax", - /*066*/ "Tenor Sax", - /*067*/ "Baritone Sax", - /*068*/ "Oboe", - /*069*/ "English Horn", - /*070*/ "Bassoon", - /*071*/ "Clarinet", - /*072*/ "Piccolo", - /*073*/ "Flute", - /*074*/ "Recorder", - /*075*/ "Pan Flute", - /*076*/ "Blown Bottle", - /*077*/ "Shakuhachi", - /*078*/ "Whistle", - /*079*/ "Ocarina", - /*080*/ "Lead 1 (square)", - /*081*/ "Lead 2 (sawtooth)", - /*082*/ "Lead 3 (calliope)", - /*083*/ "Lead 4 (chiff)", - /*084*/ "Lead 5 (charang)", - /*085*/ "Lead 6 (voice)", - /*086*/ "Lead 7 (fifths)", - /*087*/ "Lead 8 (bass+lead)", - /*088*/ "Pad 1 (new age)", - /*089*/ "Pad 2 (warm)", - /*090*/ "Pad 3 (polysynth)", - /*091*/ "Pad 4 (choir)", - /*092*/ "Pad 5 (bowed)", - /*093*/ "Pad 6 (metallic)", - /*094*/ "Pad 7 (halo)", - /*095*/ "Pad 8 (sweep)", - /*096*/ "FX 1 (rain)", - /*097*/ "FX 2 (soundtrack)", - /*098*/ "FX 3 (crystal)", - /*099*/ "FX 4 (atmosphere)", - /*100*/ "FX 5 (brightness)", - /*101*/ "FX 6 (goblins)", - /*102*/ "FX 7 (echoes)", - /*103*/ "FX 8 (sci-fi)", - /*104*/ "Sitar", - /*105*/ "Banjo", - /*106*/ "Shamisen", - /*107*/ "Koto", - /*108*/ "Kalimba", - /*109*/ "Bag pipe", - /*110*/ "Fiddle", - /*111*/ "Shannai", - /*112*/ "Tinkle Bell", - /*113*/ "Agogo", - /*114*/ "Steel Drums", - /*115*/ "Woodblock", - /*116*/ "Taiko Drum", - /*117*/ "Melodic Tom", - /*118*/ "Synth Drum", - /*119*/ "Reverse Cymbal", - /*120*/ "Guitar Fret Noise", - /*121*/ "Breath Noise", - /*122*/ "Seashore", - /*123*/ "Bird Tweet", - /*124*/ "Telephone Ring", - /*125*/ "Helicopter", - /*126*/ "Applause", - /*127*/ "Gunshot" -}; - -/* The GM Percussion map is downwards compatible to the MT32 map, which is used in SCI */ -static const char *GmPercussionNames[] = { - /*00*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /*10*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /*20*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /*30*/ 0, 0, 0, 0, - /* The preceeding percussions are not covered by the GM standard */ - /*34*/ "Acoustic Bass Drum", - /*35*/ "Bass Drum 1", - /*36*/ "Side Stick", - /*37*/ "Acoustic Snare", - /*38*/ "Hand Clap", - /*39*/ "Electric Snare", - /*40*/ "Low Floor Tom", - /*41*/ "Closed Hi-Hat", - /*42*/ "High Floor Tom", - /*43*/ "Pedal Hi-Hat", - /*44*/ "Low Tom", - /*45*/ "Open Hi-Hat", - /*46*/ "Low-Mid Tom", - /*47*/ "Hi-Mid Tom", - /*48*/ "Crash Cymbal 1", - /*49*/ "High Tom", - /*50*/ "Ride Cymbal 1", - /*51*/ "Chinese Cymbal", - /*52*/ "Ride Bell", - /*53*/ "Tambourine", - /*54*/ "Splash Cymbal", - /*55*/ "Cowbell", - /*56*/ "Crash Cymbal 2", - /*57*/ "Vibraslap", - /*58*/ "Ride Cymbal 2", - /*59*/ "Hi Bongo", - /*60*/ "Low Bongo", - /*61*/ "Mute Hi Conga", - /*62*/ "Open Hi Conga", - /*63*/ "Low Conga", - /*64*/ "High Timbale", - /*65*/ "Low Timbale", - /*66*/ "High Agogo", - /*67*/ "Low Agogo", - /*68*/ "Cabasa", - /*69*/ "Maracas", - /*70*/ "Short Whistle", - /*71*/ "Long Whistle", - /*72*/ "Short Guiro", - /*73*/ "Long Guiro", - /*74*/ "Claves", - /*75*/ "Hi Wood Block", - /*76*/ "Low Wood Block", - /*77*/ "Mute Cuica", - /*78*/ "Open Cuica", - /*79*/ "Mute Triangle", - /*80*/ "Open Triangle" -}; - -/******************************************* - * Fancy instrument mappings begin here... * - *******************************************/ - - -static const Mt32ToGmMap Mt32PresetTimbreMaps[] = { - /*000*/ {"AcouPiano1", 0, MIDI_UNMAPPED}, - /*001*/ {"AcouPiano2", 1, MIDI_UNMAPPED}, - /*002*/ {"AcouPiano3", 0, MIDI_UNMAPPED}, - /*003*/ {"ElecPiano1", 4, MIDI_UNMAPPED}, - /*004*/ {"ElecPiano2", 5, MIDI_UNMAPPED}, - /*005*/ {"ElecPiano3", 4, MIDI_UNMAPPED}, - /*006*/ {"ElecPiano4", 5, MIDI_UNMAPPED}, - /*007*/ {"Honkytonk ", 3, MIDI_UNMAPPED}, - /*008*/ {"Elec Org 1", 16, MIDI_UNMAPPED}, - /*009*/ {"Elec Org 2", 17, MIDI_UNMAPPED}, - /*010*/ {"Elec Org 3", 18, MIDI_UNMAPPED}, - /*011*/ {"Elec Org 4", 18, MIDI_UNMAPPED}, - /*012*/ {"Pipe Org 1", 19, MIDI_UNMAPPED}, - /*013*/ {"Pipe Org 2", 19, MIDI_UNMAPPED}, - /*014*/ {"Pipe Org 3", 20, MIDI_UNMAPPED}, - /*015*/ {"Accordion ", 21, MIDI_UNMAPPED}, - /*016*/ {"Harpsi 1 ", 6, MIDI_UNMAPPED}, - /*017*/ {"Harpsi 2 ", 6, MIDI_UNMAPPED}, - /*018*/ {"Harpsi 3 ", 6, MIDI_UNMAPPED}, - /*019*/ {"Clavi 1 ", 7, MIDI_UNMAPPED}, - /*020*/ {"Clavi 2 ", 7, MIDI_UNMAPPED}, - /*021*/ {"Clavi 3 ", 7, MIDI_UNMAPPED}, - /*022*/ {"Celesta 1 ", 8, MIDI_UNMAPPED}, - /*023*/ {"Celesta 2 ", 8, MIDI_UNMAPPED}, - /*024*/ {"Syn Brass1", 62, MIDI_UNMAPPED}, - /*025*/ {"Syn Brass2", 63, MIDI_UNMAPPED}, - /*026*/ {"Syn Brass3", 62, MIDI_UNMAPPED}, - /*027*/ {"Syn Brass4", 63, MIDI_UNMAPPED}, - /*028*/ {"Syn Bass 1", 38, MIDI_UNMAPPED}, - /*029*/ {"Syn Bass 2", 39, MIDI_UNMAPPED}, - /*030*/ {"Syn Bass 3", 38, MIDI_UNMAPPED}, - /*031*/ {"Syn Bass 4", 39, MIDI_UNMAPPED}, - /*032*/ {"Fantasy ", 88, MIDI_UNMAPPED}, - /*033*/ {"Harmo Pan ", 89, MIDI_UNMAPPED}, - /*034*/ {"Chorale ", 52, MIDI_UNMAPPED}, - /*035*/ {"Glasses ", 98, MIDI_UNMAPPED}, - /*036*/ {"Soundtrack", 97, MIDI_UNMAPPED}, - /*037*/ {"Atmosphere", 99, MIDI_UNMAPPED}, - /*038*/ {"Warm Bell ", 89, MIDI_UNMAPPED}, - /*039*/ {"Funny Vox ", 85, MIDI_UNMAPPED}, - /*040*/ {"Echo Bell ", 39, MIDI_UNMAPPED}, - /*041*/ {"Ice Rain ", 101, MIDI_UNMAPPED}, - /*042*/ {"Oboe 2001 ", 68, MIDI_UNMAPPED}, - /*043*/ {"Echo Pan ", 87, MIDI_UNMAPPED}, - /*044*/ {"DoctorSolo", 86, MIDI_UNMAPPED}, - /*045*/ {"Schooldaze", 103, MIDI_UNMAPPED}, - /*046*/ {"BellSinger", 88, MIDI_UNMAPPED}, - /*047*/ {"SquareWave", 80, MIDI_UNMAPPED}, - /*048*/ {"Str Sect 1", 48, MIDI_UNMAPPED}, - /*049*/ {"Str Sect 2", 48, MIDI_UNMAPPED}, - /*050*/ {"Str Sect 3", 49, MIDI_UNMAPPED}, - /*051*/ {"Pizzicato ", 45, MIDI_UNMAPPED}, - /*052*/ {"Violin 1 ", 40, MIDI_UNMAPPED}, - /*053*/ {"Violin 2 ", 40, MIDI_UNMAPPED}, - /*054*/ {"Cello 1 ", 42, MIDI_UNMAPPED}, - /*055*/ {"Cello 2 ", 42, MIDI_UNMAPPED}, - /*056*/ {"Contrabass", 43, MIDI_UNMAPPED}, - /*057*/ {"Harp 1 ", 46, MIDI_UNMAPPED}, - /*058*/ {"Harp 2 ", 46, MIDI_UNMAPPED}, - /*059*/ {"Guitar 1 ", 24, MIDI_UNMAPPED}, - /*060*/ {"Guitar 2 ", 25, MIDI_UNMAPPED}, - /*061*/ {"Elec Gtr 1", 26, MIDI_UNMAPPED}, - /*062*/ {"Elec Gtr 2", 27, MIDI_UNMAPPED}, - /*063*/ {"Sitar ", 104, MIDI_UNMAPPED}, - /*064*/ {"Acou Bass1", 32, MIDI_UNMAPPED}, - /*065*/ {"Acou Bass2", 33, MIDI_UNMAPPED}, - /*066*/ {"Elec Bass1", 34, MIDI_UNMAPPED}, - /*067*/ {"Elec Bass2", 39, MIDI_UNMAPPED}, - /*068*/ {"Slap Bass1", 36, MIDI_UNMAPPED}, - /*069*/ {"Slap Bass2", 37, MIDI_UNMAPPED}, - /*070*/ {"Fretless 1", 35, MIDI_UNMAPPED}, - /*071*/ {"Fretless 2", 35, MIDI_UNMAPPED}, - /*072*/ {"Flute 1 ", 73, MIDI_UNMAPPED}, - /*073*/ {"Flute 2 ", 73, MIDI_UNMAPPED}, - /*074*/ {"Piccolo 1 ", 72, MIDI_UNMAPPED}, - /*075*/ {"Piccolo 2 ", 72, MIDI_UNMAPPED}, - /*076*/ {"Recorder ", 74, MIDI_UNMAPPED}, - /*077*/ {"Panpipes ", 75, MIDI_UNMAPPED}, - /*078*/ {"Sax 1 ", 64, MIDI_UNMAPPED}, - /*079*/ {"Sax 2 ", 65, MIDI_UNMAPPED}, - /*080*/ {"Sax 3 ", 66, MIDI_UNMAPPED}, - /*081*/ {"Sax 4 ", 67, MIDI_UNMAPPED}, - /*082*/ {"Clarinet 1", 71, MIDI_UNMAPPED}, - /*083*/ {"Clarinet 2", 71, MIDI_UNMAPPED}, - /*084*/ {"Oboe ", 68, MIDI_UNMAPPED}, - /*085*/ {"Engl Horn ", 69, MIDI_UNMAPPED}, - /*086*/ {"Bassoon ", 70, MIDI_UNMAPPED}, - /*087*/ {"Harmonica ", 22, MIDI_UNMAPPED}, - /*088*/ {"Trumpet 1 ", 56, MIDI_UNMAPPED}, - /*089*/ {"Trumpet 2 ", 56, MIDI_UNMAPPED}, - /*090*/ {"Trombone 1", 57, MIDI_UNMAPPED}, - /*091*/ {"Trombone 2", 57, MIDI_UNMAPPED}, - /*092*/ {"Fr Horn 1 ", 60, MIDI_UNMAPPED}, - /*093*/ {"Fr Horn 2 ", 60, MIDI_UNMAPPED}, - /*094*/ {"Tuba ", 58, MIDI_UNMAPPED}, - /*095*/ {"Brs Sect 1", 61, MIDI_UNMAPPED}, - /*096*/ {"Brs Sect 2", 61, MIDI_UNMAPPED}, - /*097*/ {"Vibe 1 ", 11, MIDI_UNMAPPED}, - /*098*/ {"Vibe 2 ", 11, MIDI_UNMAPPED}, - /*099*/ {"Syn Mallet", 15, MIDI_UNMAPPED}, - /*100*/ {"Wind Bell ", 88, MIDI_UNMAPPED}, - /*101*/ {"Glock ", 9, MIDI_UNMAPPED}, - /*102*/ {"Tube Bell ", 14, MIDI_UNMAPPED}, - /*103*/ {"Xylophone ", 13, MIDI_UNMAPPED}, - /*104*/ {"Marimba ", 12, MIDI_UNMAPPED}, - /*105*/ {"Koto ", 107, MIDI_UNMAPPED}, - /*106*/ {"Sho ", 111, MIDI_UNMAPPED}, - /*107*/ {"Shakuhachi", 77, MIDI_UNMAPPED}, - /*108*/ {"Whistle 1 ", 78, MIDI_UNMAPPED}, - /*109*/ {"Whistle 2 ", 78, MIDI_UNMAPPED}, - /*110*/ {"BottleBlow", 76, MIDI_UNMAPPED}, - /*111*/ {"BreathPipe", 121, MIDI_UNMAPPED}, - /*112*/ {"Timpani ", 47, MIDI_UNMAPPED}, - /*113*/ {"MelodicTom", 117, MIDI_UNMAPPED}, - /*114*/ {"Deep Snare", MIDI_MAPPED_TO_RHYTHM, 37}, - /*115*/ {"Elec Perc1", 115, MIDI_UNMAPPED}, /* ? */ - /*116*/ {"Elec Perc2", 118, MIDI_UNMAPPED}, /* ? */ - /*117*/ {"Taiko ", 116, MIDI_UNMAPPED}, - /*118*/ {"Taiko Rim ", 118, MIDI_UNMAPPED}, - /*119*/ {"Cymbal ", MIDI_MAPPED_TO_RHYTHM, 50}, - /*120*/ {"Castanets ", MIDI_UNMAPPED, MIDI_UNMAPPED}, - /*121*/ {"Triangle ", 112, MIDI_UNMAPPED}, - /*122*/ {"Orche Hit ", 55, MIDI_UNMAPPED}, - /*123*/ {"Telephone ", 124, MIDI_UNMAPPED}, - /*124*/ {"Bird Tweet", 123, MIDI_UNMAPPED}, - /*125*/ {"OneNoteJam", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? */ - /*126*/ {"WaterBells", 98, MIDI_UNMAPPED}, - /*127*/ {"JungleTune", MIDI_UNMAPPED, MIDI_UNMAPPED} /* ? */ -}; - -static const Mt32ToGmMap Mt32RhythmTimbreMaps[] = { - /*00*/ {"Acou BD ", MIDI_MAPPED_TO_RHYTHM, 34}, - /*01*/ {"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 37}, - /*02*/ {"Acou HiTom", 117, 49}, - /*03*/ {"AcouMidTom", 117, 46}, - /*04*/ {"AcouLowTom", 117, 40}, - /*05*/ {"Elec SD ", MIDI_MAPPED_TO_RHYTHM, 39}, - /*06*/ {"Clsd HiHat", MIDI_MAPPED_TO_RHYTHM, 41}, - /*07*/ {"OpenHiHat1", MIDI_MAPPED_TO_RHYTHM, 45}, - /*08*/ {"Crash Cym ", MIDI_MAPPED_TO_RHYTHM, 48}, - /*09*/ {"Ride Cym ", MIDI_MAPPED_TO_RHYTHM, 50}, - /*10*/ {"Rim Shot ", MIDI_MAPPED_TO_RHYTHM, 36}, - /*11*/ {"Hand Clap ", MIDI_MAPPED_TO_RHYTHM, 38}, - /*12*/ {"Cowbell ", MIDI_MAPPED_TO_RHYTHM, 55}, - /*13*/ {"Mt HiConga", MIDI_MAPPED_TO_RHYTHM, 61}, - /*14*/ {"High Conga", MIDI_MAPPED_TO_RHYTHM, 62}, - /*15*/ {"Low Conga ", MIDI_MAPPED_TO_RHYTHM, 63}, - /*16*/ {"Hi Timbale", MIDI_MAPPED_TO_RHYTHM, 64}, - /*17*/ {"LowTimbale", MIDI_MAPPED_TO_RHYTHM, 65}, - /*18*/ {"High Bongo", MIDI_MAPPED_TO_RHYTHM, 59}, - /*19*/ {"Low Bongo ", MIDI_MAPPED_TO_RHYTHM, 60}, - /*20*/ {"High Agogo", 113, 66}, - /*21*/ {"Low Agogo ", 113, 67}, - /*22*/ {"Tambourine", MIDI_MAPPED_TO_RHYTHM, 53}, - /*23*/ {"Claves ", MIDI_MAPPED_TO_RHYTHM, 74}, - /*24*/ {"Maracas ", MIDI_MAPPED_TO_RHYTHM, 69}, - /*25*/ {"SmbaWhis L", 78, 71}, - /*26*/ {"SmbaWhis S", 78, 70}, - /*27*/ {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 68}, - /*28*/ {"Quijada ", MIDI_MAPPED_TO_RHYTHM, 72}, - /*29*/ {"OpenHiHat2", MIDI_MAPPED_TO_RHYTHM, 43} -}; - -static const uint8 Mt32PresetRhythmKeymap[] = { - MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, - MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, - MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, - MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, 34, 34, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 50, MIDI_UNMAPPED, MIDI_UNMAPPED, 53, MIDI_UNMAPPED, 55, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, 59, - 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, - 70, 71, 72, MIDI_UNMAPPED, 74, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, - MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, - MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, - MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, - MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, - MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED, MIDI_UNMAPPED -}; - -/* +++ - Don't change unless you've got a good reason - ++ - Looks good, sounds ok - + - Not too bad, but is it right? - ? - Where do I map this one? - ?? - Any good ideas? - ??? - I'm clueless? - R - Rhythm... */ -static const Mt32ToGmMap Mt32MemoryTimbreMaps[] = { - {"AccPnoKA2 ", 1, MIDI_UNMAPPED}, /* ++ (KQ1) */ - {"Acou BD ", MIDI_MAPPED_TO_RHYTHM, 34}, /* R (PQ2) */ - {"Acou SD ", MIDI_MAPPED_TO_RHYTHM, 37}, /* R (PQ2) */ - {"AcouPnoKA ", 0, MIDI_UNMAPPED}, /* ++ (KQ1) */ - {"BASS ", 32, MIDI_UNMAPPED}, /* + (LSL3) */ - {"BASSOONPCM", 70, MIDI_UNMAPPED}, /* + (CB) */ - {"BEACH WAVE", 122, MIDI_UNMAPPED}, /* + (LSL3) */ - {"BagPipes ", 109, MIDI_UNMAPPED}, - {"BassPizzMS", 45, MIDI_UNMAPPED}, /* ++ (HQ) */ - {"BassoonKA ", 70, MIDI_UNMAPPED}, /* ++ (KQ1) */ - {"Bell MS", 112, MIDI_UNMAPPED}, /* ++ (iceMan) */ - {"Bells MS", 112, MIDI_UNMAPPED}, /* + (HQ) */ - {"Big Bell ", 14, MIDI_UNMAPPED}, /* + (CB) */ - {"Bird Tweet", 123, MIDI_UNMAPPED}, - {"BrsSect MS", 61, MIDI_UNMAPPED}, /* +++ (iceMan) */ - {"CLAPPING ", 126, MIDI_UNMAPPED}, /* ++ (LSL3) */ - {"Cabasa ", MIDI_MAPPED_TO_RHYTHM, 68}, /* R (HBoG) */ - {"Calliope ", 82, MIDI_UNMAPPED}, /* +++ (HQ) */ - {"CelticHarp", 46, MIDI_UNMAPPED}, /* ++ (CoC) */ - {"Chicago MS", 1, MIDI_UNMAPPED}, /* ++ (iceMan) */ - {"Chop ", 117, MIDI_UNMAPPED}, - {"Chorale MS", 52, MIDI_UNMAPPED}, /* + (CoC) */ - {"ClarinetMS", 71, MIDI_UNMAPPED}, - {"Claves ", MIDI_MAPPED_TO_RHYTHM, 74}, /* R (PQ2) */ - {"Claw MS", 118, MIDI_UNMAPPED}, /* + (HQ) */ - {"ClockBell ", 14, MIDI_UNMAPPED}, /* + (CB) */ - {"ConcertCym", MIDI_MAPPED_TO_RHYTHM, 54}, /* R ? (KQ1) */ - {"Conga MS", MIDI_MAPPED_TO_RHYTHM, 63}, /* R (HQ) */ - {"CoolPhone ", 124, MIDI_UNMAPPED}, /* ++ (LSL3) */ - {"CracklesMS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */ - {"CreakyD MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */ - {"Cricket ", 120, MIDI_UNMAPPED}, /* ? (CB) */ - {"CrshCymbMS", MIDI_MAPPED_TO_RHYTHM, 56}, /* R +++ (iceMan) */ - {"CstlGateMS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */ - {"CymSwellMS", MIDI_MAPPED_TO_RHYTHM, 54}, /* R ? (CoC, HQ) */ - {"CymbRollKA", MIDI_MAPPED_TO_RHYTHM, 56}, /* R ? (KQ1) */ - {"Cymbal Lo ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* R ? (LSL3) */ - {"card ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HBoG) */ - {"DirtGtr MS", 30, MIDI_UNMAPPED}, /* + (iceMan) */ - {"DirtGtr2MS", 29, MIDI_UNMAPPED}, /* + (iceMan) */ - {"E Bass MS", 33, MIDI_UNMAPPED}, /* + (SQ3) */ - {"ElecBassMS", 33, MIDI_UNMAPPED}, - {"ElecGtr MS", 27, MIDI_UNMAPPED}, /* ++ (iceMan) */ - {"EnglHornMS", 69, MIDI_UNMAPPED}, - {"FantasiaKA", 88, MIDI_UNMAPPED}, - {"Fantasy ", 99, MIDI_UNMAPPED}, /* + (PQ2) */ - {"Fantasy2MS", 99, MIDI_UNMAPPED}, /* ++ (CoC, HQ) */ - {"Filter MS", 95, MIDI_UNMAPPED}, /* +++ (iceMan) */ - {"Filter2 MS", 95, MIDI_UNMAPPED}, /* ++ (iceMan) */ - {"Flame2 MS", 121, MIDI_UNMAPPED}, /* ? (HQ) */ - {"Flames MS", 121, MIDI_UNMAPPED}, /* ? (HQ) */ - {"Flute MS", 73, MIDI_UNMAPPED}, /* +++ (HQ) */ - {"FogHorn MS", 58, MIDI_UNMAPPED}, - {"FrHorn1 MS", 60, MIDI_UNMAPPED}, /* +++ (HQ) */ - {"FunnyTrmp ", 56, MIDI_UNMAPPED}, /* ++ (CB) */ - {"GameSnd MS", 80, MIDI_UNMAPPED}, - {"Glock MS", 9, MIDI_UNMAPPED}, /* +++ (HQ) */ - {"Gunshot ", 127, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Hammer MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ) */ - {"Harmonica2", 22, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Harpsi 1 ", 6, MIDI_UNMAPPED}, /* + (HBoG) */ - {"Harpsi 2 ", 6, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Heart MS", 116, MIDI_UNMAPPED}, /* ? (iceMan) */ - {"Horse1 MS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */ - {"Horse2 MS", 115, MIDI_UNMAPPED}, /* ? (CoC, HQ) */ - {"InHale MS", 121, MIDI_UNMAPPED}, /* ++ (iceMan) */ - {"KNIFE ", 120, MIDI_UNMAPPED}, /* ? (LSL3) */ - {"KenBanjo ", 105, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Kiss MS", 25, MIDI_UNMAPPED}, /* ++ (HQ) */ - {"KongHit ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ??? (KQ1) */ - {"Koto ", 107, MIDI_UNMAPPED}, /* +++ (PQ2) */ - {"Laser MS", 81, MIDI_UNMAPPED}, /* ?? (HQ) */ - {"Meeps MS", 62, MIDI_UNMAPPED}, /* ? (HQ) */ - {"MTrak MS", 62, MIDI_UNMAPPED}, /* ?? (iceMan) */ - {"MachGun MS", 127, MIDI_UNMAPPED}, /* ? (iceMan) */ - {"OCEANSOUND", 122, MIDI_UNMAPPED}, /* + (LSL3) */ - {"Oboe 2001 ", 68, MIDI_UNMAPPED}, /* + (PQ2) */ - {"Ocean MS", 122, MIDI_UNMAPPED}, /* + (iceMan) */ - {"PPG 2.3 MS", 75, MIDI_UNMAPPED}, /* ? (iceMan) */ - {"PianoCrank", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */ - {"PicSnareMS", MIDI_MAPPED_TO_RHYTHM, 39}, /* R ? (iceMan) */ - {"PiccoloKA ", 72, MIDI_UNMAPPED}, /* +++ (KQ1) */ - {"PinkBassMS", 39, MIDI_UNMAPPED}, - {"Pizz2 ", 45, MIDI_UNMAPPED}, /* ++ (CB) */ - {"Portcullis", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */ - {"Raspbry MS", 81, MIDI_UNMAPPED}, /* ? (HQ) */ - {"RatSqueek ", 72, MIDI_UNMAPPED}, /* ? (CB, CoC) */ - {"Record78 ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* +++ (CB) */ - {"RecorderMS", 74, MIDI_UNMAPPED}, /* +++ (CoC) */ - {"Red Baron ", 125, MIDI_UNMAPPED}, /* ? (CB) */ - {"ReedPipMS ", 20, MIDI_UNMAPPED}, /* +++ (Coc) */ - {"RevCymb MS", 119, MIDI_UNMAPPED}, - {"RifleShot ", 127, MIDI_UNMAPPED}, /* + (CB) */ - {"RimShot MS", MIDI_MAPPED_TO_RHYTHM, 36}, /* R */ - {"SHOWER ", 52, MIDI_UNMAPPED}, /* ? (LSL3) */ - {"SQ Bass MS", 32, MIDI_UNMAPPED}, /* + (SQ3) */ - {"ShakuVibMS", 79, MIDI_UNMAPPED}, /* + (iceMan) */ - {"SlapBassMS", 36, MIDI_UNMAPPED}, /* +++ (iceMan) */ - {"Snare MS", MIDI_MAPPED_TO_RHYTHM, 37}, /* R (HQ) */ - {"Some Birds", 123, MIDI_UNMAPPED}, /* + (CB) */ - {"Sonar MS", 78, MIDI_UNMAPPED}, /* ? (iceMan) */ - {"Soundtrk2 ", 97, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Soundtrack", 97, MIDI_UNMAPPED}, /* ++ (CoC) */ - {"SqurWaveMS", 80, MIDI_UNMAPPED}, - {"StabBassMS", 34, MIDI_UNMAPPED}, /* + (iceMan) */ - {"SteelDrmMS", 114, MIDI_UNMAPPED}, /* +++ (iceMan) */ - {"StrSect1MS", 48, MIDI_UNMAPPED}, /* ++ (HQ) */ - {"String MS", 45, MIDI_UNMAPPED}, /* + (CoC) */ - {"Syn-Choir ", 91, MIDI_UNMAPPED}, - {"Syn Brass4", 63, MIDI_UNMAPPED}, /* ++ (PQ2) */ - {"SynBass MS", 38, MIDI_UNMAPPED}, - {"SwmpBackgr", 120, MIDI_UNMAPPED}, /* ?? (CB,HQ) */ - {"T-Bone2 MS", 57, MIDI_UNMAPPED}, /* +++ (HQ) */ - {"Taiko ", 116, 34}, /* +++ (Coc) */ - {"Taiko Rim ", 118, 36}, /* +++ (LSL3) */ - {"Timpani1 ", 47, MIDI_UNMAPPED}, /* +++ (CB) */ - {"Tom MS", 117, 47}, /* +++ (iceMan) */ - {"Toms MS", 117, 47}, /* +++ (CoC, HQ) */ - {"Tpt1prtl ", 56, MIDI_UNMAPPED}, /* +++ (KQ1) */ - {"TriangleMS", 112, 80}, /* R (CoC) */ - {"Trumpet 1 ", 56, MIDI_UNMAPPED}, /* +++ (CoC) */ - {"Type MS", 114, MIDI_UNMAPPED}, /* ? (iceMan) */ - {"WaterBells", 98, MIDI_UNMAPPED}, /* + (PQ2) */ - {"WaterFallK", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (KQ1) */ - {"Whiporill ", 123, MIDI_UNMAPPED}, /* + (CB) */ - {"Wind ", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CB) */ - {"Wind MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (HQ, iceMan) */ - {"Wind2 MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC) */ - {"Woodpecker", 115, MIDI_UNMAPPED}, /* ? (CB) */ - {"WtrFall MS", MIDI_UNMAPPED, MIDI_UNMAPPED}, /* ? (CoC, HQ, iceMan) */ - {0, 0, 0} -}; - -} // End of namespace Sci diff --git a/engines/sci/sound/softseq/midi.cpp b/engines/sci/sound/softseq/midi.cpp deleted file mode 100644 index d32d0c529b..0000000000 --- a/engines/sci/sound/softseq/midi.cpp +++ /dev/null @@ -1,859 +0,0 @@ -/* 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. - * - * $URL$ - * $Id$ - * - */ - -#include "sci/sci.h" - -#include "common/config-manager.h" -#include "sound/fmopl.h" -#include "sound/softsynth/emumidi.h" - -#include "sci/resource.h" -#include "sci/sound/softseq/mididriver.h" -#include "sci/sound/softseq/map-mt32-to-gm.h" - -namespace Sci { - -class MidiPlayer_Midi : public MidiPlayer { -public: - enum { - kVoices = 32, - kReverbConfigNr = 11, - kMaxSysExSize = 264 - }; - - MidiPlayer_Midi(); - virtual ~MidiPlayer_Midi(); - - int open(ResourceManager *resMan); - void close(); - void send(uint32 b); - void sysEx(const byte *msg, uint16 length); - bool hasRhythmChannel() const { return true; } - byte getPlayId(SciVersion soundVersion); - int getPolyphony() const { return kVoices; } - void setVolume(byte volume); - int getVolume(); - void setReverb(byte reverb); - void playSwitch(bool play); - -private: - bool isMt32GmPatch(const byte *data, int size); - void readMt32GmPatch(const byte *data, int size); - void readMt32Patch(const byte *data, int size); - void readMt32DrvData(); - - void mapMt32ToGm(byte *data, size_t size); - uint8 lookupGmInstrument(const char *iname); - uint8 lookupGmRhythmKey(const char *iname); - uint8 getGmInstrument(const Mt32ToGmMap &Mt32Ins); - - void sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay); - void sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay); - void setMt32Volume(byte volume); - void resetMt32(); - - void noteOn(int channel, int note, int velocity); - void setPatch(int channel, int patch); - void controlChange(int channel, int control, int value); - - struct Channel { - byte mappedPatch; - byte patch; - int velocityMapIdx; - bool playing; - int8 keyShift; - int8 volAdjust; - uint8 pan; - uint8 hold; - uint8 volume; - - Channel() : mappedPatch(MIDI_UNMAPPED), patch(MIDI_UNMAPPED), velocityMapIdx(0), playing(false), - keyShift(0), volAdjust(0), pan(0x80), hold(0), volume(0x7f) { } - }; - - bool _isMt32; - bool _isOldPatchFormat; - bool _hasReverb; - bool _playSwitch; - int _masterVolume; - - byte _reverbConfig[kReverbConfigNr][3]; - Channel _channels[16]; - uint8 _percussionMap[128]; - int8 _keyShift[128]; - int8 _volAdjust[128]; - uint8 _patchMap[128]; - uint8 _velocityMapIdx[128]; - uint8 _velocityMap[4][128]; - - // These are extensions used for our own MT-32 to GM mapping - uint8 _pitchBendRange[128]; - uint8 _percussionVelocityScale[128]; - - byte _goodbyeMsg[20]; - byte _sysExBuf[kMaxSysExSize]; -}; - -MidiPlayer_Midi::MidiPlayer_Midi() : _playSwitch(true), _masterVolume(15), _isMt32(false), _hasReverb(false), _isOldPatchFormat(true) { - MidiDriverType midiType = MidiDriver::detectMusicDriver(MDT_MIDI); - _driver = createMidi(midiType); - - if (midiType == MD_MT32 || ConfMan.getBool("native_mt32")) - _isMt32 = true; - - _sysExBuf[0] = 0x41; - _sysExBuf[1] = 0x10; - _sysExBuf[2] = 0x16; - _sysExBuf[3] = 0x12; -} - -MidiPlayer_Midi::~MidiPlayer_Midi() { - delete _driver; -} - -void MidiPlayer_Midi::noteOn(int channel, int note, int velocity) { - uint8 patch = _channels[channel].mappedPatch; - - if (channel == MIDI_RHYTHM_CHANNEL) { - if (_percussionMap[note] == MIDI_UNMAPPED) { - debugC(kDebugLevelSound, "[Midi] Percussion instrument %i is unmapped", note); - return; - } - - note = _percussionMap[note]; - // Scale velocity; - velocity = velocity * _percussionVelocityScale[note] / 127; - } else if (patch >= 128) { - if (patch == MIDI_UNMAPPED) - return; - - // Map to rhythm - channel = MIDI_RHYTHM_CHANNEL; - note = patch - 128; - - // Scale velocity; - velocity = velocity * _percussionVelocityScale[note] / 127; - } else { - int8 keyshift = _keyShift[channel]; - - int shiftNote = note + keyshift; - - if (keyshift > 0) { - while (shiftNote > 127) - shiftNote -= 12; - } else { - while (shiftNote < 0) - shiftNote += 12; - } - - note = shiftNote; - - // We assume that velocity 0 maps to 0 (for note off) - int mapIndex = _channels[channel].velocityMapIdx; - velocity = _velocityMap[mapIndex][velocity]; - } - - _channels[channel].playing = true; - _driver->send(0x90 | channel, note, velocity); -} - -void MidiPlayer_Midi::controlChange(int channel, int control, int value) { - switch (control) { - case 0x07: - _channels[channel].volume = value; - - if (!_playSwitch) - return; - - value += _channels[channel].volAdjust; - - if (value > 0x7f) - value = 0x7f; - - if (value < 0) - value = 1; - - value *= _masterVolume; - - if (value != 0) { - value /= 15; - - if (value == 0) - value = 1; - } - break; - case 0x0a: - if (_channels[channel].pan == value) - return; - - _channels[channel].pan = value; - break; - case 0x40: - if (_channels[channel].hold == value) - return; - - _channels[channel].hold = value; - break; - case 0x7b: - if (!_channels[channel].playing) - return; - - _channels[channel].playing = false; - } - - _driver->send(0xb0 | channel, control, value); -} - -void MidiPlayer_Midi::setPatch(int channel, int patch) { - bool resetVol = false; - - if ((channel == MIDI_RHYTHM_CHANNEL) || (_channels[channel].patch == patch)) - return; - - _channels[channel].patch = patch; - _channels[channel].velocityMapIdx = _velocityMapIdx[patch]; - - if (_channels[channel].mappedPatch == MIDI_UNMAPPED) - resetVol = true; - - _channels[channel].mappedPatch = _patchMap[patch]; - - if (_patchMap[patch] == MIDI_UNMAPPED) { - debugC(kDebugLevelSound, "[Midi] Channel %i set to unmapped patch %i", channel, patch); - _driver->send(0xb0 | channel, 0x7b, 0); - _driver->send(0xb0 | channel, 0x40, 0); - return; - } - - if (_channels[channel].keyShift != _keyShift[patch]) { - _channels[channel].keyShift = _keyShift[patch]; - _driver->send(0xb0 | channel, 0x7b, 0); - _driver->send(0xb0 | channel, 0x40, 0); - resetVol = true; - } - - if (resetVol || (_channels[channel].volAdjust != _volAdjust[patch])) { - _channels[channel].volAdjust = _volAdjust[patch]; - controlChange(channel, 0x07, _channels[channel].volume); - } - - uint8 bendRange = _pitchBendRange[patch]; - if (bendRange != MIDI_UNMAPPED) - _driver->setPitchBendRange(channel, bendRange); - - _driver->send(0xc0 | channel, _patchMap[patch], 0); -} - -void MidiPlayer_Midi::send(uint32 b) { - byte command = b & 0xf0; - byte channel = b & 0xf; - byte op1 = (b >> 8) & 0x7f; - byte op2 = (b >> 16) & 0x7f; - - // In early SCI0, we may also get events for AdLib rhythm channels. - // While an MT-32 would ignore those with the default channel mapping, - // we filter these out for the benefit of other MIDI devices. - if (channel < 1 || channel > 9) - return; - - switch (command) { - case 0x80: - noteOn(channel, op1, 0); - break; - case 0x90: - noteOn(channel, op1, op2); - break; - case 0xb0: - controlChange(channel, op1, op2); - break; - case 0xc0: - setPatch(channel, op1); - break; - case 0xe0: - _driver->send(b); - break; - default: - warning("Ignoring MIDI event %02x", command); - } -} - -void MidiPlayer_Midi::setVolume(byte volume) { - _masterVolume = volume; - - if (!_playSwitch) - return; - - for (uint i = 1; i < 10; i++) { - if (_channels[i].volume != 0xff) - controlChange(i, 0x07, _channels[i].volume & 0x7f); - } -} - -int MidiPlayer_Midi::getVolume() { - return _masterVolume; -} - -void MidiPlayer_Midi::setReverb(byte reverb) { - _reverb = CLIP(reverb, 0, kReverbConfigNr - 1); - if (_hasReverb) - sendMt32SysEx(0x100001, _reverbConfig[_reverb], 3, true); -} - -void MidiPlayer_Midi::playSwitch(bool play) { - _playSwitch = play; - if (play) - setVolume(_masterVolume); - else { - for (uint i = 1; i < 10; i++) - _driver->send(0xb0 | i, 7, 0); - } -} - -bool MidiPlayer_Midi::isMt32GmPatch(const byte *data, int size) -{ - if (size < 1155) - return false; - if (size > 16889) - return true; - - bool isMt32 = false; - bool isMt32Gm = false; - - if (READ_LE_UINT16(data + 1153) + 1155 == size) - isMt32Gm = true; - - int pos = 492 + 246 * data[491]; - - if ((size >= (pos + 386)) && (READ_BE_UINT16(data + pos) == 0xabcd)) - pos += 386; - - if ((size >= (pos + 267)) && (READ_BE_UINT16(data + pos) == 0xdcba)) - pos += 267; - - if (size == pos) - isMt32 = true; - - if (isMt32 == isMt32Gm) - error("Failed to detect MT-32 patch format"); - - return isMt32Gm; -} - -void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, Common::SeekableReadStream *str, int len, bool noDelay = false) { - if (len + 8 > kMaxSysExSize) { - warning("SysEx message exceed maximum size; ignoring"); - return; - } - - uint16 chk = 0; - - _sysExBuf[4] = (addr >> 16) & 0xff; - _sysExBuf[5] = (addr >> 8) & 0xff; - _sysExBuf[6] = addr & 0xff; - - for (int i = 0; i < len; i++) - _sysExBuf[7 + i] = str->readByte(); - - for (int i = 4; i < 7 + len; i++) - chk += _sysExBuf[i]; - - _sysExBuf[7 + len] = 128 - chk % 128; - - if (noDelay) - _driver->sysEx(_sysExBuf, len + 8); - else - sysEx(_sysExBuf, len + 8); -} - -void MidiPlayer_Midi::sendMt32SysEx(const uint32 addr, const byte *buf, int len, bool noDelay = false) { - Common::MemoryReadStream *str = new Common::MemoryReadStream(buf, len); - sendMt32SysEx(addr, str, len, noDelay); - delete str; -} - -void MidiPlayer_Midi::readMt32Patch(const byte *data, int size) { - Common::MemoryReadStream *str = new Common::MemoryReadStream(data, size); - - // Send before-SysEx text - str->seek(0x14); - sendMt32SysEx(0x200000, str, 20); - - // Save goodbye message - str->read(_goodbyeMsg, 20); - - byte volume = CLIP(str->readUint16LE(), 0, 100); - setMt32Volume(volume); - - // Reverb default only used in (roughly) SCI0/SCI01 - _reverb = str->readByte(); - _hasReverb = true; - - // Skip reverb SysEx message - str->seek(11, SEEK_CUR); - - // Read reverb data - for (int i = 0; i < kReverbConfigNr; i++) { - _reverbConfig[i][0] = str->readByte(); - _reverbConfig[i][1] = str->readByte(); - _reverbConfig[i][2] = str->readByte(); - } - - // Patches 1-48 - sendMt32SysEx(0x50000, str, 256); - sendMt32SysEx(0x50200, str, 128); - - // Timbres - byte timbresNr = str->readByte(); - for (int i = 0; i < timbresNr; i++) - sendMt32SysEx(0x80000 + (i << 9), str, 246); - - uint16 flag = str->readUint16BE(); - - if (!str->eos() && (flag == 0xabcd)) { - // Patches 49-96 - sendMt32SysEx(0x50300, str, 256); - sendMt32SysEx(0x50500, str, 128); - flag = str->readUint16BE(); - } - - if (!str->eos() && (flag == 0xdcba)) { - // Rhythm key map - sendMt32SysEx(0x30110, str, 256); - // Partial reserve - sendMt32SysEx(0x100004, str, 9); - } - - // Send after-SysEx text - str->seek(0); - sendMt32SysEx(0x200000, str, 20); - - // Send the mystery SysEx - sendMt32SysEx(0x52000a, (const byte *)"\x16\x16\x16\x16\x16\x16", 6); - - delete str; -} - -void MidiPlayer_Midi::readMt32GmPatch(const byte *data, int size) { - memcpy(_patchMap, data, 0x80); - memcpy(_keyShift, data + 0x80, 0x80); - memcpy(_volAdjust, data + 0x100, 0x80); - memcpy(_percussionMap, data + 0x180, 0x80); - _channels[MIDI_RHYTHM_CHANNEL].volAdjust = data[0x200]; - memcpy(_velocityMapIdx, data + 0x201, 0x80); - memcpy(_velocityMap, data + 0x281, 0x200); - - uint16 midiSize = READ_LE_UINT16(data + 0x481); - - if (midiSize > 0) { - if (size < midiSize + 1155) - error("Failed to read MIDI data"); - - const byte *midi = data + 1155; - byte command = 0; - uint i = 0; - - while (i < midiSize) { - byte op1, op2; - - if (midi[i] & 0x80) - command = midi[i++]; - - switch (command & 0xf0) { - case 0xf0: { - byte *sysExEnd = (byte *)memchr(midi + i, 0xf7, midiSize - i); - - if (!sysExEnd) - error("Failed to find end of sysEx"); - - int len = sysExEnd - (midi + i); - sysEx(midi + i, len); - - i += len + 1; // One more for the 0x7f - break; - } - case 0x80: - case 0x90: - case 0xa0: - case 0xb0: - case 0xe0: - if (i + 1 >= midiSize) - error("MIDI command exceeds data size"); - - op1 = midi[i++]; - op2 = midi[i++]; - _driver->send(command, op1, op2); - break; - case 0xc0: - case 0xd0: - if (i >= midiSize) - error("MIDI command exceeds data size"); - - op1 = midi[i++]; - _driver->send(command, op1, 0); - break; - default: - error("Failed to find MIDI command byte"); - } - } - } -} - -void MidiPlayer_Midi::readMt32DrvData() { - Common::File f; - - if (f.open("MT32.DRV")) { - int size = f.size(); - - assert(size >= 166); - - // Send before-SysEx text - f.seek(0x59); - - // Skip 2 extra 0 bytes in some drivers - if (f.readUint16LE() != 0) - f.seek(-2, SEEK_CUR); - - sendMt32SysEx(0x200000, static_cast(&f), 20); - - // Send after-SysEx text (SSCI sends this before every song) - sendMt32SysEx(0x200000, static_cast(&f), 20); - - // Save goodbye message - f.read(_goodbyeMsg, 20); - - // Set volume - byte volume = CLIP(f.readUint16LE(), 0, 100); - setMt32Volume(volume); - - byte reverbSysEx[13]; - // This old driver should have a full reverb SysEx - if ((f.read(reverbSysEx, 13) != 13) || (reverbSysEx[0] != 0xf0) || (reverbSysEx[12] != 0xf7)) - error("Error reading MT32.DRV"); - - // Send reverb SysEx - sysEx(reverbSysEx + 1, 11); - _hasReverb = false; - - f.seek(0x29); - - // Read AdLib->MT-32 patch map - for (int i = 0; i < 48; i++) { - _patchMap[i] = f.readByte(); - } - - f.close(); - } else { - error("Failed to open MT32.DRV"); - } -} - -byte MidiPlayer_Midi::lookupGmInstrument(const char *iname) { - int i = 0; - - while (Mt32MemoryTimbreMaps[i].name) { - if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0) - return getGmInstrument(Mt32MemoryTimbreMaps[i]); - i++; - } - return MIDI_UNMAPPED; -} - -byte MidiPlayer_Midi::lookupGmRhythmKey(const char *iname) { - int i = 0; - - while (Mt32MemoryTimbreMaps[i].name) { - if (scumm_strnicmp(iname, Mt32MemoryTimbreMaps[i].name, 10) == 0) - return Mt32MemoryTimbreMaps[i].gmRhythmKey; - i++; - } - return MIDI_UNMAPPED; -} - -uint8 MidiPlayer_Midi::getGmInstrument(const Mt32ToGmMap &Mt32Ins) { - if (Mt32Ins.gmInstr == MIDI_MAPPED_TO_RHYTHM) - return Mt32Ins.gmRhythmKey + 0x80; - else - return Mt32Ins.gmInstr; -} - -void MidiPlayer_Midi::mapMt32ToGm(byte *data, size_t size) { - // FIXME: Clean this up - int memtimbres, patches; - uint8 group, number, keyshift, finetune, bender_range; - uint8 *patchpointer; - uint32 pos; - int i; - - for (i = 0; i < 128; i++) { - _patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[i]); - _pitchBendRange[i] = 12; - } - - for (i = 0; i < 128; i++) - _percussionMap[i] = Mt32PresetRhythmKeymap[i]; - - memtimbres = *(data + 0x1eb); - pos = 0x1ec + memtimbres * 0xf6; - - if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xabcd)) { - patches = 96; - pos += 2 + 8 * 48; - } else - patches = 48; - - debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Patches detected", patches); - debugC(kDebugLevelSound, "[MT32-to-GM] %d MT-32 Memory Timbres", memtimbres); - - debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping patches.."); - - for (i = 0; i < patches; i++) { - char name[11]; - - if (i < 48) - patchpointer = data + 0x6b + 8 * i; - else - patchpointer = data + 0x1ec + 8 * (i - 48) + memtimbres * 0xf6 + 2; - - group = *patchpointer; - number = *(patchpointer + 1); - keyshift = *(patchpointer + 2); - finetune = *(patchpointer + 3); - bender_range = *(patchpointer + 4); - - debugCN(kDebugLevelSound, " [%03d] ", i); - - switch (group) { - case 1: - number += 64; - // Fall through - case 0: - _patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[number]); - debugCN(kDebugLevelSound, "%s -> ", Mt32PresetTimbreMaps[number].name); - break; - case 2: - strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10); - name[10] = 0; - _patchMap[i] = lookupGmInstrument(name); - debugCN(kDebugLevelSound, "%s -> ", name); - break; - case 3: - _patchMap[i] = getGmInstrument(Mt32RhythmTimbreMaps[number]); - debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number].name); - break; - default: - break; - } - - if (_patchMap[i] == MIDI_UNMAPPED) { - debugC(kDebugLevelSound, "[Unmapped]"); - } else { - if (_patchMap[i] >= 128) { - debugC(kDebugLevelSound, "%s [Rhythm]", GmPercussionNames[_patchMap[i] - 128]); - } else { - debugC(kDebugLevelSound, "%s", GmInstrumentNames[_patchMap[i]]); - } - } - - _keyShift[i] = CLIP(keyshift, 0, 48) - 24; - _pitchBendRange[i] = CLIP(bender_range, 0, 24); - } - - if (size > pos && ((0x100 * *(data + pos) + *(data + pos + 1)) == 0xdcba)) { - debugC(kDebugLevelSound, "\n[MT32-to-GM] Mapping percussion.."); - - for (i = 0; i < 64 ; i++) { - number = *(data + pos + 4 * i + 2); - - debugCN(kDebugLevelSound, " [%03d] ", i + 23); - - if (number < 64) { - char name[11]; - strncpy(name, (const char *)data + 0x1ec + number * 0xf6, 10); - name[10] = 0; - debugCN(kDebugLevelSound, "%s -> ", name); - _percussionMap[i + 23] = lookupGmRhythmKey(name); - } else { - if (number < 94) { - debugCN(kDebugLevelSound, "%s -> ", Mt32RhythmTimbreMaps[number - 64].name); - _percussionMap[i + 23] = Mt32RhythmTimbreMaps[number - 64].gmRhythmKey; - } else { - debugCN(kDebugLevelSound, "[Key %03i] -> ", number); - _percussionMap[i + 23] = MIDI_UNMAPPED; - } - } - - if (_percussionMap[i + 23] == MIDI_UNMAPPED) - debugC(kDebugLevelSound, "[Unmapped]"); - else - debugC(kDebugLevelSound, "%s", GmPercussionNames[_percussionMap[i + 23]]); - - _percussionVelocityScale[i + 23] = *(data + pos + 4 * i + 3) * 127 / 100; - } - } -} - -void MidiPlayer_Midi::setMt32Volume(byte volume) { - sendMt32SysEx(0x100016, &volume, 1); -} - -void MidiPlayer_Midi::resetMt32() { - sendMt32SysEx(0x7f0000, (const byte *)"\x01\x00", 2, true); - - // This seems to require a longer delay than usual - g_system->delayMillis(150); -} - -int MidiPlayer_Midi::open(ResourceManager *resMan) { - assert(resMan != NULL); - - int retval = _driver->open(); - if (retval != 0) { - warning("Failed to open MIDI driver"); - return retval; - } - - // By default use no mapping - for (uint i = 0; i < 128; i++) { - _percussionMap[i] = i; - _patchMap[i] = i; - _velocityMap[0][i] = i; - _keyShift[i] = 0; - _volAdjust[i] = 0; - _velocityMapIdx[i] = 0; - _pitchBendRange[i] = MIDI_UNMAPPED; - _percussionVelocityScale[i] = 127; - } - - Resource *res = NULL; - - if (_isMt32) { - // MT-32 - resetMt32(); - - res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); - - if (res) { - if (isMt32GmPatch(res->data, res->size)) { - readMt32GmPatch(res->data, res->size); - strncpy((char *)_goodbyeMsg, " ScummVM ", 20); - } else { - readMt32Patch(res->data, res->size); - } - } else { - readMt32DrvData(); - } - } else { - // General MIDI - res = resMan->findResource(ResourceId(kResourceTypePatch, 4), 0); - - if (res && isMt32GmPatch(res->data, res->size)) { - // There is a GM patch - readMt32GmPatch(res->data, res->size); - - // Detect the format of patch 1, so that we know what play mask to use - res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); - if (!res) - _isOldPatchFormat = false; - else - _isOldPatchFormat = !isMt32GmPatch(res->data, res->size); - } else { - // No GM patch found, map instruments using MT-32 patch - - warning("Game has no native support for General MIDI, applying auto-mapping"); - - // Modify velocity map to make low velocity notes a little louder - for (uint i = 1; i < 0x40; i++) - _velocityMap[0][i] = 0x20 + (i - 1) / 2; - - res = resMan->findResource(ResourceId(kResourceTypePatch, 1), 0); - - if (res) { - if (!isMt32GmPatch(res->data, res->size)) - mapMt32ToGm(res->data, res->size); - else - error("MT-32 patch has wrong type"); - } else { - // No MT-32 patch present, try to read from MT32.DRV - Common::File f; - - if (f.open("MT32.DRV")) { - int size = f.size(); - - assert(size >= 70); - - f.seek(0x29); - - // Read AdLib->MT-32 patch map - for (int i = 0; i < 48; i++) - _patchMap[i] = getGmInstrument(Mt32PresetTimbreMaps[f.readByte() & 0x7f]); - } - } - } - } - - return 0; -} - -void MidiPlayer_Midi::close() { - if (_isMt32) { - // Send goodbye message - sendMt32SysEx(0x200000, _goodbyeMsg, 20); - } - - _driver->close(); -} - -void MidiPlayer_Midi::sysEx(const byte *msg, uint16 length) { - _driver->sysEx(msg, length); - - // Wait the time it takes to send the SysEx data - uint32 delay = (length + 2) * 1000 / 3125; - - // Plus an additional delay for the MT-32 rev00 - if (_isMt32) - delay += 40; - - g_system->delayMillis(delay); - g_system->updateScreen(); -} - -byte MidiPlayer_Midi::getPlayId(SciVersion soundVersion) { - switch (soundVersion) { - case SCI_VERSION_0_EARLY: - case SCI_VERSION_0_LATE: - return 0x01; - default: - if (_isMt32) - return 0x0c; - else - return _isOldPatchFormat ? 0x0c : 0x07; - } -} - -MidiPlayer *MidiPlayer_Midi_create() { - return new MidiPlayer_Midi(); -} - -} // End of namespace Sci diff --git a/engines/sci/sound/softseq/mididriver.h b/engines/sci/sound/softseq/mididriver.h deleted file mode 100644 index a1e6390a06..0000000000 --- a/engines/sci/sound/softseq/mididriver.h +++ /dev/null @@ -1,120 +0,0 @@ -/* 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. - * - * $URL$ - * $Id$ - * - */ - -#ifndef SCI_SFX_SOFTSEQ_MIDIDRIVER_H -#define SCI_SFX_SOFTSEQ_MIDIDRIVER_H - -#include "sci/sci.h" -#include "sound/mididrv.h" -#include "sound/softsynth/emumidi.h" -#include "common/error.h" - -namespace Sci { - -class ResourceManager; - -enum { - MIDI_CHANNELS = 16, - MIDI_PROP_MASTER_VOLUME = 0 -}; - - -#define MIDI_RHYTHM_CHANNEL 9 - -/* Special SCI sound stuff */ - -#define SCI_MIDI_TIME_EXPANSION_PREFIX 0xF8 -#define SCI_MIDI_TIME_EXPANSION_LENGTH 240 - -#define SCI_MIDI_EOT 0xFC -#define SCI_MIDI_SET_SIGNAL 0xCF -#define SCI_MIDI_SET_POLYPHONY 0x4B -#define SCI_MIDI_RESET_ON_SUSPEND 0x4C -#define SCI_MIDI_CHANNEL_MUTE 0x4E -#define SCI_MIDI_SET_REVERB 0x50 -#define SCI_MIDI_HOLD 0x52 -#define SCI_MIDI_CUMULATIVE_CUE 0x60 -#define SCI_MIDI_CHANNEL_SOUND_OFF 0x78 /* all-sound-off for Bn */ -#define SCI_MIDI_CHANNEL_NOTES_OFF 0x7B /* all-notes-off for Bn */ - -#define SCI_MIDI_SET_SIGNAL_LOOP 0x7F -/* If this is the parameter of 0xCF, the loop point is set here */ - -#define SCI_MIDI_CONTROLLER(status) ((status & 0xF0) == 0xB0) - -class MidiPlayer : public MidiDriver { -protected: - MidiDriver *_driver; - byte _reverb; - -public: - MidiPlayer() : _reverb(0) { } - - int open() { - ResourceManager *resMan = ((SciEngine *)g_engine)->getResourceManager(); // HACK - return open(resMan); - } - virtual int open(ResourceManager *resMan) { return _driver->open(); } - virtual void close() { _driver->close(); } - virtual void send(uint32 b) { _driver->send(b); } - uint32 getBaseTempo() { return _driver->getBaseTempo(); } - virtual bool hasRhythmChannel() const = 0; - MidiChannel *allocateChannel() { return _driver->allocateChannel(); } - MidiChannel *getPercussionChannel() { return _driver->getPercussionChannel(); } - void setTimerCallback(void *timer_param, Common::TimerManager::TimerProc timer_proc) { _driver->setTimerCallback(timer_param, timer_proc); } - - virtual byte getPlayId(SciVersion soundVersion) = 0; - virtual int getPolyphony() const = 0; - - virtual void setVolume(byte volume) { - if(_driver) - _driver->property(MIDI_PROP_MASTER_VOLUME, volume); - } - - virtual int getVolume() { - return _driver ? _driver->property(MIDI_PROP_MASTER_VOLUME, 0xffff) : 0; - } - - virtual byte getReverb() { return _reverb; } - virtual void setReverb(byte reverb) { _reverb = reverb; } - - virtual void playSwitch(bool play) { - if (!play) { - // Send "All Sound Off" on all channels - for (int i = 0; i < MIDI_CHANNELS; ++i) - _driver->send(0xb0 + i, SCI_MIDI_CHANNEL_NOTES_OFF, 0); - } - } -}; - -extern MidiPlayer *MidiPlayer_AdLib_create(); -extern MidiPlayer *MidiPlayer_Amiga_create(); -extern MidiPlayer *MidiPlayer_PCJr_create(); -extern MidiPlayer *MidiPlayer_PCSpeaker_create(); -extern MidiPlayer *MidiPlayer_Midi_create(); - -} // End of namespace Sci - -#endif // SCI_SFX_SOFTSEQ_MIDIDRIVER_H diff --git a/engines/sci/sound/softseq/pcjr.cpp b/engines/sci/sound/softseq/pcjr.cpp deleted file mode 100644 index 476526b06e..0000000000 --- a/engines/sci/sound/softseq/pcjr.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* 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. - * - * $URL$ - * $Id$ - * - */ - -#include "sci/sound/softseq/mididriver.h" - -namespace Sci { - -#define VOLUME_SHIFT 3 - -#define BASE_NOTE 129 // A10 -#define BASE_OCTAVE 10 // A10, as I said - -const static int freq_table[12] = { // A4 is 440Hz, halftone map is x |-> ** 2^(x/12) - 28160, // A10 - 29834, - 31608, - 33488, - 35479, - 37589, - 39824, - 42192, - 44701, - 47359, - 50175, - 53159 -}; - -static inline int get_freq(int note) { - int halftone_delta = note - BASE_NOTE; - int oct_diff = ((halftone_delta + BASE_OCTAVE * 12) / 12) - BASE_OCTAVE; - int halftone_index = (halftone_delta + (12 * 100)) % 12 ; - int freq = (!note) ? 0 : freq_table[halftone_index] / (1 << (-oct_diff)); - - return freq; -} - -class MidiDriver_PCJr : public MidiDriver_Emulated { -public: - friend class MidiPlayer_PCJr; - - enum { - kMaxChannels = 3 - }; - - MidiDriver_PCJr(Audio::Mixer *mixer) : MidiDriver_Emulated(mixer) { } - ~MidiDriver_PCJr() { } - - // MidiDriver - int open() { return open(kMaxChannels); } - void close(); - void send(uint32 b); - MidiChannel *allocateChannel() { return NULL; } - MidiChannel *getPercussionChannel() { return NULL; } - - // AudioStream - bool isStereo() const { return false; } - int getRate() const { return _mixer->getOutputRate(); } - - // MidiDriver_Emulated - void generateSamples(int16 *buf, int len); - - int open(int channels); -private: - int _channels_nr; - int _global_volume; // Base volume - int _volumes[kMaxChannels]; - int _notes[kMaxChannels]; // Current halftone, or 0 if off - int _freq_count[kMaxChannels]; - int _channel_assigner; - int _channels_assigned; - int _chan_nrs[kMaxChannels]; -}; - -void MidiDriver_PCJr::send(uint32 b) { - byte command = b & 0xff; - byte op1 = (b >> 8) & 0xff; - byte op2 = (b >> 16) & 0xff; - int i; - int mapped_chan = -1; - int chan_nr = command & 0xf; - - // First, test for channel having been assigned already - if (_channels_assigned & (1 << chan_nr)) { - // Already assigned this channel number: - for (i = 0; i < _channels_nr; i++) - if (_chan_nrs[i] == chan_nr) { - mapped_chan = i; - break; - } - } else if ((command & 0xe0) == 0x80) { - // Assign new channel round-robin - - // Mark channel as unused: - if (_chan_nrs[_channel_assigner] >= 0) - _channels_assigned &= ~(1 << _chan_nrs[_channel_assigner]); - - // Remember channel: - _chan_nrs[_channel_assigner] = chan_nr; - // Mark channel as used - _channels_assigned |= (1 << _chan_nrs[_channel_assigner]); - - // Save channel for use later in this call: - mapped_chan = _channel_assigner; - // Round-ropin iterate channel assigner: - _channel_assigner = (_channel_assigner + 1) % _channels_nr; - } - - if (mapped_chan == -1) - return; - - switch (command & 0xf0) { - - case 0x80: - if (op1 == _notes[mapped_chan]) - _notes[mapped_chan] = 0; - break; - - case 0x90: - if (!op2) { - if (op1 == _notes[mapped_chan]) - _notes[mapped_chan] = 0; - } else { - _notes[mapped_chan] = op1; - _volumes[mapped_chan] = op2; - } - break; - - case 0xb0: - if ((op1 == SCI_MIDI_CHANNEL_NOTES_OFF) || (op1 == SCI_MIDI_CHANNEL_SOUND_OFF)) - _notes[mapped_chan] = 0; - break; - - default: - debug(2, "Unused MIDI command %02x %02x %02x", command, op1, op2); - break; /* ignore */ - } -} - -void MidiDriver_PCJr::generateSamples(int16 *data, int len) { - int i; - int chan; - int freq[kMaxChannels]; - int frequency = getRate(); - - for (chan = 0; chan < _channels_nr; chan++) - freq[chan] = get_freq(_notes[chan]); - - for (i = 0; i < len; i++) { - int16 result = 0; - - for (chan = 0; chan < _channels_nr; chan++) - if (_notes[chan]) { - int volume = (_global_volume * _volumes[chan]) - >> VOLUME_SHIFT; - - _freq_count[chan] += freq[chan]; - while (_freq_count[chan] >= (frequency << 1)) - _freq_count[chan] -= (frequency << 1); - - if (_freq_count[chan] - freq[chan] < 0) { - /* Unclean rising edge */ - int l = volume << 1; - result += -volume + (l * _freq_count[chan]) / freq[chan]; - } else if (_freq_count[chan] >= frequency - && _freq_count[chan] - freq[chan] < frequency) { - /* Unclean falling edge */ - int l = volume << 1; - result += volume - (l * (_freq_count[chan] - frequency)) / freq[chan]; - } else { - if (_freq_count[chan] < frequency) - result += volume; - else - result += -volume; - } - } - data[i] = result; - } -} - -int MidiDriver_PCJr::open(int channels) { - if (_isOpen) - return MERR_ALREADY_OPEN; - - if (channels > kMaxChannels) - return -1; - - _channels_nr = channels; - _global_volume = 100; - for (int i = 0; i < _channels_nr; i++) { - _volumes[i] = 100; - _notes[i] = 0; - _freq_count[i] = 0; - _chan_nrs[i] = -1; - } - _channel_assigner = 0; - _channels_assigned = 0; - - MidiDriver_Emulated::open(); - - _mixer->playInputStream(Audio::Mixer::kPlainSoundType, &_mixerSoundHandle, this, -1, _mixer->kMaxChannelVolume, 0, DisposeAfterUse::NO); - - return 0; -} - -void MidiDriver_PCJr::close() { - _mixer->stopHandle(_mixerSoundHandle); -} - -class MidiPlayer_PCJr : public MidiPlayer { -public: - MidiPlayer_PCJr() { _driver = new MidiDriver_PCJr(g_system->getMixer()); } - int open(ResourceManager *resMan) { return static_cast(_driver)->open(getPolyphony()); } - byte getPlayId(SciVersion soundVersion); - int getPolyphony() const { return 3; } - bool hasRhythmChannel() const { return false; } - void setVolume(byte volume) { static_cast(_driver)->_global_volume = volume; } -}; - -byte MidiPlayer_PCJr::getPlayId(SciVersion soundVersion) { - switch (soundVersion) { - case SCI_VERSION_0_EARLY: - return 0x02; - case SCI_VERSION_0_LATE: - return 0x10; - default: - return 0x13; - } -} - -MidiPlayer *MidiPlayer_PCJr_create() { - return new MidiPlayer_PCJr(); -} - -class MidiPlayer_PCSpeaker : public MidiPlayer_PCJr { -public: - byte getPlayId(SciVersion soundVersion); - int getPolyphony() const { return 1; } -}; - -byte MidiPlayer_PCSpeaker::getPlayId(SciVersion soundVersion) { - switch (soundVersion) { - case SCI_VERSION_0_EARLY: - return 0x04; - case SCI_VERSION_0_LATE: - return 0x20; - default: - return 0x12; - } -} - -MidiPlayer *MidiPlayer_PCSpeaker_create() { - return new MidiPlayer_PCSpeaker(); -} - -} // End of namespace Sci -- cgit v1.2.3