scummvm/scumm/instrument.cpp

458 lines
11 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001-2005 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*/
#include "stdafx.h"
#include "scumm/scumm.h"
#include "scumm/saveload.h"
#include "scumm/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) {
warning ("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