From 81e8a2860ee066d6b863bf80bb99a592dec4cbe0 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 20 Feb 2006 20:57:26 +0000 Subject: Moved iMUSE code to the new directory engines/scumm/imuse/ svn-id: r20801 --- engines/scumm/imuse/instrument.cpp | 462 +++++++++++++++++++++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 engines/scumm/imuse/instrument.cpp (limited to 'engines/scumm/imuse/instrument.cpp') diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp new file mode 100644 index 0000000000..16d89c5ed2 --- /dev/null +++ b/engines/scumm/imuse/instrument.cpp @@ -0,0 +1,462 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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 "common/stdafx.h" +#include "scumm/scumm.h" +#include "scumm/saveload.h" +#include "scumm/imuse/instrument.h" +#include "sound/mididrv.h" + +namespace Scumm { + +static bool _native_mt32 = false; + +static struct { + const char *name; + byte program; +} + +roland_to_gm_map [] = { + // Monkey Island 2 instruments + // TODO: Complete + { "badspit ", 62 }, + { "Big Drum ", 116 }, + { "burp ", 58 }, +// { "dinkfall ", ??? }, +// { "Fire Pit ", ??? }, + { "foghorn ", 60 }, + { "glop ", 39 }, +// { "jacob's la", ??? }, + { "LeshBass ", 33 }, +// { "lowsnort ", ??? }, + { "ML explosn", 127 }, + { "ReggaeBass", 32 }, +// { "rope fall ", ??? }, + { "rumble ", 89 }, + { "SdTrk Bend", 97 }, +// { "snort ", ??? }, + { "spitting ", 62 }, + { "Swell 1 ", 95 }, + { "Swell 2 ", 95 }, + { "thnderclap", 127 } + + // Fate of Atlantis instruments + // TODO: Build +// { "*aah! ", ??? }, +// { "*ooh! ", ??? }, +// { "*ShotFar4 ", ??? }, +// { "*splash3 ", ??? }, +// { "*torpedo5 ", ??? }, +// { "*whip3 ", ??? }, +// { "*woodknock", ??? }, +// { "35 lavabub", ??? }, +// { "49 bzzt! ", ??? }, +// { "applause ", ??? }, +// { "Arabongo ", ??? }, +// { "Big Drum ", ??? }, // DUPLICATE (todo: confirm) +// { "bodythud1 ", ??? }, +// { "boneKLOK2 ", ??? }, +// { "boom10 ", ??? }, +// { "boom11 ", ??? }, +// { "boom15 ", ??? }, +// { "boxclik1a ", ??? }, +// { "brassbonk3", ??? }, +// { "carstart ", ??? }, +// { "cb tpt 2 ", ??? }, +// { "cell door ", ??? }, +// { "chains ", ??? }, +// { "crash ", ??? }, +// { "crsrt/idl3", ??? }, +// { "Fire Pit ", ??? }, // DUPLICATE (todo: confirm) +// { "Fzooom ", ??? }, +// { "Fzooom 2 ", ??? }, +// { "ghostwhosh", ??? }, +// { "glasssmash", ??? }, +// { "gloop2 ", ??? }, +// { "gunShotNea", ??? }, +// { "idoorclse ", ??? }, +// { "knife ", ??? }, +// { "lavacmbl4 ", ??? }, +// { "Mellow Str", ??? }, +// { "mtlheater1", ??? }, +// { "pachinko5 ", ??? }, +// { "Ping1 ", ??? }, +// { "rockcrunch", ??? }, +// { "rumble ", ??? }, // DUPLICATE (todo: confirm) +// { "runngwatr ", ??? }, +// { "scrape2 ", ??? }, +// { "snakeHiss ", ??? }, +// { "snort ", ??? }, // DUPLICATE (todo: confirm) +// { "spindle4 ", ??? }, +// { "splash2 ", ??? }, +// { "squirel ", ??? }, +// { "steam3 ", ??? }, +// { "stonwheel6", ??? }, +// { "street ", ??? }, +// { "trickle4 ", ??? } +}; + +const byte Instrument::_gmRhythmMap[35] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 39, 40, 41, 66, 47, + 65, 48, 56}; + // This emulates the percussion bank setup LEC used with the MT-32, + // where notes 24 - 34 were assigned instruments without reverb. + // It also fixes problems on GS devices that map sounds to these + // notes by default. + +class Instrument_Program : public InstrumentInternal { +private: + byte _program; + bool _mt32; + +public: + Instrument_Program (byte program, bool mt32); + Instrument_Program (Serializer *s); + void saveOrLoad (Serializer *s); + void send (MidiChannel *mc); + void copy_to (Instrument *dest) { dest->program (_program, _mt32); } + bool is_valid() { + return (_program < 128) && + ((_native_mt32 == _mt32) || _native_mt32 + ? (MidiDriver::_gmToMt32[_program] < 128) + : (MidiDriver::_mt32ToGm[_program] < 128)); } +}; + +class Instrument_Adlib : public InstrumentInternal { +private: + struct { + byte flags_1; + byte oplvl_1; + byte atdec_1; + byte sustrel_1; + byte waveform_1; + byte flags_2; + byte oplvl_2; + byte atdec_2; + byte sustrel_2; + byte waveform_2; + byte feedback; + byte flags_a; + struct { byte a,b,c,d,e,f,g,h; } extra_a; + byte flags_b; + struct { byte a,b,c,d,e,f,g,h; } extra_b; + byte duration; + } _instrument; + +public: + Instrument_Adlib (byte *data); + Instrument_Adlib (Serializer *s); + void saveOrLoad (Serializer *s); + void send (MidiChannel *mc); + void copy_to (Instrument *dest) { dest->adlib ((byte *) &_instrument); } + bool is_valid() { return true; } +}; + +class Instrument_Roland : public InstrumentInternal { +private: + struct RolandInstrument { + byte roland_id; + byte device_id; + byte model_id; + byte command; + byte address[3]; + struct { + byte name[10]; + byte partial_struct12; + byte partial_struct34; + byte partial_mute; + byte env_mode; + } common; + struct { + byte wg_pitch_coarse; + byte wg_pitch_fine; + byte wg_pitch_keyfollow; + byte wg_pitch_bender_sw; + byte wg_waveform_pcm_bank; + byte wg_pcm_wave_num; + byte wg_pulse_width; + byte wg_pw_velo_sens; + byte p_env_depth; + byte p_evn_velo_sens; + byte p_env_time_keyf; + byte p_env_time[4]; + byte p_env_level[3]; + byte p_env_sustain_level; + byte end_level; + byte p_lfo_rate; + byte p_lfo_depth; + byte p_lfo_mod_sens; + byte tvf_cutoff_freq; + byte tvf_resonance; + byte tvf_keyfollow; + byte tvf_bias_point_dir; + byte tvf_bias_level; + byte tvf_env_depth; + byte tvf_env_velo_sens; + byte tvf_env_depth_keyf; + byte tvf_env_time_keyf; + byte tvf_env_time[5]; + byte tvf_env_level[3]; + byte tvf_env_sustain_level; + byte tva_level; + byte tva_velo_sens; + byte tva_bias_point_1; + byte tva_bias_level_1; + byte tva_bias_point_2; + byte tva_bias_level_2; + byte tva_env_time_keyf; + byte tva_env_time_v_follow; + byte tva_env_time[5]; + byte tva_env_level[3]; + byte tva_env_sustain_level; + } partial[4]; + byte checksum; + } GNUPACK; + RolandInstrument _instrument; + + char _instrument_name [11]; + + uint8 getEquivalentGM(); + +public: + Instrument_Roland (byte *data); + Instrument_Roland (Serializer *s); + void saveOrLoad (Serializer *s); + void send (MidiChannel *mc); + void copy_to (Instrument *dest) { dest->roland ((byte *) &_instrument); } + bool is_valid() { return (_native_mt32 ? true : (_instrument_name[0] != '\0')); } +}; + +//////////////////////////////////////// +// +// Instrument class members +// +//////////////////////////////////////// + +void Instrument::nativeMT32 (bool native) { + _native_mt32 = native; +} + +void Instrument::clear() { + if (_instrument) + delete _instrument; + _instrument = NULL; + _type = itNone; +} + +void Instrument::program (byte prog, bool mt32) { + clear(); + if (prog > 127) + return; + _type = itProgram; + _instrument = new Instrument_Program (prog, mt32); +} + +void Instrument::adlib (byte *instrument) { + clear(); + if (!instrument) + return; + _type = itAdlib; + _instrument = new Instrument_Adlib (instrument); +} + +void Instrument::roland (byte *instrument) { + clear(); + if (!instrument) + return; + _type = itRoland; + _instrument = new Instrument_Roland (instrument); +} + +void Instrument::saveOrLoad (Serializer *s) { + if (s->isSaving()) { + s->saveByte (_type); + if (_instrument) + _instrument->saveOrLoad (s); + } else { + clear(); + _type = s->loadByte(); + switch (_type) { + case itNone: + break; + case itProgram: + _instrument = new Instrument_Program (s); + break; + case itAdlib: + _instrument = new Instrument_Adlib (s); + break; + case itRoland: + _instrument = new Instrument_Roland (s); + break; + default: + warning ("No known instrument classification #%d", (int) _type); + _type = itNone; + } + } +} + +//////////////////////////////////////// +// +// Instrument_Program class members +// +//////////////////////////////////////// + +Instrument_Program::Instrument_Program (byte program, bool mt32) : +_program (program), +_mt32 (mt32) { + if (program > 127) + _program = 255; +} + +Instrument_Program::Instrument_Program (Serializer *s) { + _program = 255; + if (!s->isSaving()) + saveOrLoad (s); +} + +void Instrument_Program::saveOrLoad (Serializer *s) { + if (s->isSaving()) { + s->saveByte (_program); + s->saveByte (_mt32 ? 1 : 0); + } else { + _program = s->loadByte(); + _mt32 = (s->loadByte() > 0); + } +} + +void Instrument_Program::send (MidiChannel *mc) { + if (_program > 127) + return; + + byte program = _program; + if (_native_mt32 != _mt32) + program = _native_mt32 ? MidiDriver::_gmToMt32 [program] : MidiDriver::_mt32ToGm [program]; + if (program < 128) + mc->programChange (program); +} + +//////////////////////////////////////// +// +// Instrument_Adlib class members +// +//////////////////////////////////////// + +Instrument_Adlib::Instrument_Adlib (byte *data) { + memcpy (&_instrument, data, sizeof (_instrument)); +} + +Instrument_Adlib::Instrument_Adlib (Serializer *s) { + if (!s->isSaving()) + saveOrLoad (s); + else + memset (&_instrument, 0, sizeof (_instrument)); +} + +void Instrument_Adlib::saveOrLoad (Serializer *s) { + if (s->isSaving()) + s->saveBytes (&_instrument, sizeof (_instrument)); + else + s->loadBytes (&_instrument, sizeof (_instrument)); +} + +void Instrument_Adlib::send (MidiChannel *mc) { + mc->sysEx_customInstrument ('ADL ', (byte *) &_instrument); +} + +//////////////////////////////////////// +// +// Instrument_Roland class members +// +//////////////////////////////////////// + +Instrument_Roland::Instrument_Roland (byte *data) { + memcpy (&_instrument, data, sizeof (_instrument)); + memcpy (&_instrument_name, &_instrument.common.name, sizeof (_instrument.common.name)); + _instrument_name[10] = '\0'; + if (!_native_mt32 && getEquivalentGM() >= 128) { + debug (0, "MT-32 instrument \"%s\" not supported yet", _instrument_name); + _instrument_name[0] = '\0'; + } +} + +Instrument_Roland::Instrument_Roland (Serializer *s) { + _instrument_name[0] = '\0'; + if (!s->isSaving()) + saveOrLoad (s); + else + memset (&_instrument, 0, sizeof (_instrument)); +} + +void Instrument_Roland::saveOrLoad (Serializer *s) { + if (s->isSaving()) { + s->saveBytes (&_instrument, sizeof (_instrument)); + } else { + s->loadBytes (&_instrument, sizeof (_instrument)); + memcpy (&_instrument_name, &_instrument.common.name, sizeof (_instrument.common.name)); + _instrument_name[10] = '\0'; + if (!_native_mt32 && getEquivalentGM() >= 128) { + debug (2, "MT-32 custom instrument \"%s\" not supported", _instrument_name); + _instrument_name[0] = '\0'; + } + } // end if +} + +void Instrument_Roland::send (MidiChannel *mc) { + if (_native_mt32) { + if (mc->getNumber() > 8) + return; + _instrument.device_id = mc->getNumber(); + + // Remap instrument to appropriate address space. + int address = 0x008000; + _instrument.address[0] = (address >> 14) & 0x7F; + _instrument.address[1] = (address >> 7) & 0x7F; + _instrument.address[2] = (address ) & 0x7F; + + // Recompute the checksum. + byte checksum = 0; + byte *ptr = (byte *) &_instrument + 4; + int i; + for (i = 4; i < (int)sizeof (_instrument) - 1; ++i) + checksum -= *ptr++; + _instrument.checksum = checksum & 0x7F; + + mc->device()->sysEx ((byte *) &_instrument, sizeof (_instrument)); + } else { + // Convert to a GM program change. + byte program = getEquivalentGM(); + if (program < 128) + mc->programChange (program); + } +} + +uint8 Instrument_Roland::getEquivalentGM() { + byte i; + for (i = 0; i != ARRAYSIZE(roland_to_gm_map); ++i) { + if (!memcmp (roland_to_gm_map[i].name, _instrument.common.name, 10)) + return roland_to_gm_map[i].program; + } + return 255; +} + +} // End of namespace Scumm -- cgit v1.2.3