mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-25 11:15:00 +00:00
4b18be1cf7
svn-id: r17601
458 lines
11 KiB
C++
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
|