scummvm/scumm/imuse.cpp
2002-10-21 07:31:51 +00:00

5822 lines
155 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2001 Ludvig Strigeus
* Copyright (C) 2001/2002 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 "sound/fmopl.h"
#include "sound/mididrv.h"
#include "scumm/imuse.h"
#include "scumm/saveload.h"
#include "scumm/sound.h"
#include "common/util.h"
/*
* Some constants
*/
#define TICKS_PER_BEAT 480
#define SYSEX_ID 0x7D
#define PERCUSSION_CHANNEL 9
#define TRIGGER_ID 0
#define COMMAND_ID 1
#define MDPG_TAG "MDpg"
#define MDHD_TAG "MDhd"
/* Roland to General Midi patch table. Still needs some work. */
static const byte mt32_to_gmidi[128] = {
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0, 1, 0, 2, 4, 4, 5, 3, 16, 17, 18, 16, 16, 19, 20, 21, // 0x
6, 6, 6, 7, 7, 7, 8, 112, 62, 62, 63, 63, 38, 38, 39, 39, // 1x
88, 54, 52, 98, 97, 99, 14, 54, 102, 96, 53, 102, 81, 100, 14, 80, // 2x
48, 48, 49, 45, 41, 40, 42, 42, 43, 46, 45, 24, 25, 28, 27, 104, // 3x
32, 32, 34, 33, 36, 37, 35, 35, 79, 73, 72, 72, 74, 75, 64, 65, // 4x
66, 67, 71, 71, 68, 69, 70, 22, 56, 59, 57, 57, 60, 60, 58, 61, // 5x
61, 11, 11, 98, 14, 9, 14, 13, 12, 107, 107, 77, 78, 78, 76, 76, // 6x
47, 117, 127, 118, 118, 116, 115, 119, 115, 112, 55, 124, 123, 0, 14, 117 // 7x
};
/* Put IMUSE specific classes here, instead of in a .h file
* they will only be used from this file, so it will reduce
* compile time */
class IMuseDriver;
struct Part;
struct MidiChannelAdl;
struct MidiChannelGM;
struct Instrument;
struct HookDatas {
byte _jump, _transpose;
byte _part_onoff[16];
byte _part_volume[16];
byte _part_program[16];
byte _part_transpose[16];
int query_param(int param, byte chan);
int set(byte cls, byte value, byte chan);
};
struct Player {
IMuseInternal *_se;
Part *_parts;
bool _active;
bool _scanning;
int _id;
byte _priority;
byte _volume;
int8 _pan;
int8 _transpose;
int8 _detune;
uint _vol_chan;
byte _vol_eff;
uint _song_index;
uint _track_index;
uint _timer_counter;
uint _loop_to_beat;
uint _loop_from_beat;
uint _loop_counter;
uint _loop_to_tick;
uint _loop_from_tick;
uint32 _tempo;
uint32 _tempo_eff; /* NoSave */
uint32 _cur_pos;
uint32 _next_pos;
uint32 _song_offset;
uint32 _timer_speed; /* NoSave */
uint _tick_index;
uint _beat_index;
uint _ticks_per_beat;
byte _speed; /* NoSave */
bool _abort;
HookDatas _hook;
byte _marker; // Sam & Max: SysEx marker
bool _mt32emulate;
bool _isGM;
/* Player part */
void hook_clear();
void clear();
bool start_sound(int sound);
void uninit_parts();
byte *parse_midi(byte *s);
void key_off(uint8 chan, byte data);
void key_on(uint8 chan, byte data, byte velocity);
void part_set_transpose(uint8 chan, byte relative, int8 b);
void parse_sysex(byte *p, uint len);
void maybe_jump (byte cmd, uint track, uint beat, uint tick);
void maybe_set_transpose(byte *data);
void maybe_part_onoff(byte *data);
void maybe_set_volume(byte *data);
void maybe_set_program(byte *data);
void maybe_set_transpose_part(byte *data);
uint update_actives();
Part *get_part(uint8 part);
void turn_off_pedals();
int set_vol(byte vol);
int get_param(int param, byte chan);
int query_part_param(int param, byte chan);
int set_transpose(byte relative, int b);
void set_priority(int pri);
void set_pan(int pan);
void set_detune(int detune);
void turn_off_parts();
void play_active_notes();
void cancel_volume_fade();
static void decode_sysex_bytes(byte *src, byte *dst, int len);
void clear_active_note(int chan, byte note);
void set_active_note(int chan, byte note);
void clear_active_notes();
/* Sequencer part */
bool set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick);
void clear_loop();
void set_speed(byte speed);
bool jump(uint track, uint beat, uint tick);
void uninit_seq();
void set_tempo(uint32 data);
int start_seq_sound(int sound);
void find_sustaining_notes(byte *a, byte *b, uint32 l);
int scan(uint totrack, uint tobeat, uint totick);
int query_param(int param);
int fade_vol(byte vol, int time);
bool is_fading_out();
void sequencer_timer();
};
struct VolumeFader {
Player *player;
bool active;
byte curvol;
uint16 speed_lo_max, num_steps;
int8 speed_hi;
int8 direction;
int8 speed_lo;
uint16 speed_lo_counter;
void initialize() {
active = false;
}
void on_timer(bool probe);
byte fading_to();
};
struct SustainingNotes {
SustainingNotes *next;
SustainingNotes *prev;
Player *player;
byte note, chan;
uint32 off_pos;
uint32 pos;
uint16 counter;
};
struct CommandQueue {
uint16 array[8];
};
struct IsNoteCmdData {
byte chan;
byte note;
byte vel;
};
struct MidiChannel {
Part *_part;
MidiChannelAdl *adl() { return (MidiChannelAdl *)this; }
MidiChannelGM *gm() { return (MidiChannelGM *)this; }
};
struct MidiChannelGM : MidiChannel {
byte _chan;
uint16 _actives[8];
};
struct Part {
int _slot;
IMuseDriver *_drv;
Part *_next, *_prev;
MidiChannel *_mc;
Player *_player;
int16 _pitchbend;
byte _pitchbend_factor;
int8 _transpose, _transpose_eff;
byte _vol, _vol_eff;
int8 _detune, _detune_eff;
int8 _pan, _pan_eff;
bool _on;
byte _modwheel;
bool _pedal;
byte _program;
int8 _pri;
byte _pri_eff;
byte _chan;
byte _effect_level;
byte _chorus;
byte _percussion;
byte _bank;
void key_on(byte note, byte velocity);
void key_off(byte note);
void set_param(byte param, int value);
void init(IMuseDriver * _driver);
void setup(Player *player);
void uninit();
void off();
void silence();
void set_instrument(uint b);
void set_instrument(Instrument * data);
void set_transpose(int8 transpose);
void set_vol(uint8 volume);
void set_detune(int8 detune);
void set_pri(int8 pri);
void set_pan(int8 pan);
void set_modwheel(uint value);
void set_pedal(bool value);
void set_pitchbend(int value);
void release_pedal();
void set_program(byte program);
void set_chorus(uint chorus);
void set_effect_level(uint level);
int update_actives(uint16 *active);
void set_pitchbend_factor(uint8 value);
void set_onoff(bool on);
void fix_after_load();
void update_pris();
void changed(uint16 what);
};
struct ImTrigger {
int sound;
byte id;
byte command [4];
};
/* Abstract IMuseInternal driver class */
class IMuseDriver {
public:
enum {
pcMod = 1,
pcVolume = 2,
pcPedal = 4,
pcModwheel = 8,
pcPan = 16,
pcEffectLevel = 32,
pcProgram = 64,
pcChorus = 128,
pcPitchBendFactor = 256,
pcAll = 511,
};
virtual void on_timer() = 0;
virtual uint32 get_base_tempo() = 0;
virtual byte get_hardware_type() = 0;
virtual void init(IMuseInternal *eng, OSystem *syst) = 0;
virtual void uninit() = 0;
virtual void update_pris() = 0;
virtual void set_instrument(uint slot, byte *instr) = 0;
virtual void part_set_instrument(Part *part, Instrument * instr) = 0;
virtual void part_key_on(Part *part, byte note, byte velocity) = 0;
virtual void part_key_off(Part *part, byte note) = 0;
virtual void part_off(Part *part) = 0;
virtual void part_changed(Part *part, uint16 what) = 0;
virtual void part_set_param(Part *part, byte param, int value) = 0;
virtual int part_update_active(Part *part, uint16 *active) = 0;
};
// WARNING: This is the internal variant of the IMUSE class.
// imuse.h contains a public version of the same class.
// the public version, only contains a set of methods.
class IMuseInternal : public IMuse {
friend struct Player;
private:
IMuseDriver * _driver;
byte **_base_sounds;
byte _locked;
byte _hardware_type;
private:
bool _paused;
bool _active_volume_faders;
bool _initialized;
byte _volume_fader_counter;
int _game_tempo;
uint _queue_end, _queue_pos, _queue_sound;
byte _queue_adding;
SustainingNotes *_sustain_notes_used;
SustainingNotes *_sustain_notes_free;
SustainingNotes *_sustain_notes_head;
byte _queue_marker;
byte _queue_cleared;
byte _master_volume; /* Master volume. 0-127 */
byte _music_volume; /* Global music volume. 0-128 */
uint16 _trigger_count;
ImTrigger _snm_triggers[16]; // Sam & Max triggers
uint16 _channel_volume[8];
uint16 _channel_volume_eff[8]; /* NoSave */
uint16 _volchan_table[8];
Player _players[8];
SustainingNotes _sustaining_notes[24];
VolumeFader _volume_fader[8];
Part _parts[32];
uint16 _active_notes[128];
CommandQueue _cmd_queue[64];
byte *findTag(int sound, char *tag, int index);
bool isMT32(int sound);
bool isGM(int sound);
int get_queue_sound_status(int sound);
Player *allocate_player(byte priority);
void handle_marker(uint id, byte data);
int get_channel_volume(uint a);
void init_players();
void init_parts();
void init_volume_fader();
void init_sustaining_notes();
void init_queue();
void sequencer_timers();
void expire_sustain_notes();
void expire_volume_faders();
Part *allocate_part(byte pri);
int enqueue_command(int a, int b, int c, int d, int e, int f, int g);
int enqueue_trigger(int sound, int marker);
int query_queue(int param);
Player *get_player_byid(int id);
int get_volchan_entry(uint a);
int set_volchan_entry(uint a, uint b);
int set_channel_volume(uint chan, uint vol);
void update_volumes();
void reset_tick();
VolumeFader *allocate_volume_fader();
int set_volchan(int sound, int volchan);
void fix_parts_after_load();
void fix_players_after_load(Scumm *scumm);
static int saveReference(IMuseInternal *me, byte type, void *ref);
static void *loadReference(IMuseInternal *me, byte type, int ref);
void lock();
void unlock();
int set_master_volume_intern(uint vol);
public:
~IMuseInternal();
Part *parts_ptr() {
return _parts;
}
IMuseDriver *driver() {
return _driver;
}
int initialize(OSystem *syst, MidiDriver *midi, SoundMixer *mixer);
/* Public interface */
void on_timer();
void pause(bool paused);
int terminate();
int save_or_load(Serializer *ser, Scumm *scumm);
int set_music_volume(uint vol);
int get_music_volume();
int set_master_volume(uint vol);
int get_master_volume();
bool start_sound(int sound);
int stop_sound(int sound);
int stop_all_sounds();
int get_sound_status(int sound);
int32 do_command(int a, int b, int c, int d, int e, int f, int g, int h);
int clear_queue();
void setBase(byte **base);
uint32 property(int prop, uint32 value);
static IMuseInternal *create(OSystem *syst, MidiDriver *midi, SoundMixer *mixer);
};
/* IMuseAdlib classes */
struct Struct10 {
byte active;
int16 cur_val;
int16 count;
uint16 param;
int16 start_value;
byte loop;
byte table_a[4];
byte table_b[4];
int8 unk3;
int8 modwheel;
int8 modwheel_last;
uint16 speed_lo_max;
uint16 num_steps;
int16 speed_hi;
int8 direction;
uint16 speed_lo;
uint16 speed_lo_counter;
};
struct Struct11 {
int16 modify_val;
byte param, flag0x40, flag0x10;
Struct10 *s10;
};
struct InstrumentExtra {
byte a, b, c, d, e, f, g, h;
};
struct Instrument {
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;
InstrumentExtra extra_a;
byte flags_b;
InstrumentExtra extra_b;
byte duration;
};
struct MidiChannelAdl : MidiChannel {
MidiChannelAdl *_next, *_prev;
byte _waitforpedal;
byte _note;
byte _channel;
byte _twochan;
byte _vol_1, _vol_2;
int16 _duration;
Struct10 _s10a;
Struct11 _s11a;
Struct10 _s10b;
Struct11 _s11b;
};
class IMuseAdlib:public IMuseDriver {
private:
FM_OPL *_opl;
byte *_adlib_reg_cache;
IMuseInternal *_se;
SoundMixer *_mixer;
int _adlib_timer_counter;
uint16 channel_table_2[9];
int _midichan_index;
int _next_tick;
uint16 curnote_table[9];
MidiChannelAdl _midi_channels[9];
Instrument _part_instr[32];
Instrument _glob_instr[32];
void adlib_key_off(int chan);
void adlib_note_on(int chan, byte note, int mod);
void adlib_note_on_ex(int chan, byte note, int mod);
int adlib_read_param(int chan, byte data);
void adlib_setup_channel(int chan, Instrument * instr, byte vol_1, byte vol_2);
byte adlib_read(byte port) {
return _adlib_reg_cache[port];
}
void adlib_set_param(int channel, byte param, int value);
void adlib_key_onoff(int channel);
void adlib_write(byte port, byte value);
void adlib_playnote(int channel, int note);
MidiChannelAdl *allocate_midichan(byte pri);
void reset_tick();
void mc_off(MidiChannel * mc);
static void link_mc(Part *part, MidiChannelAdl *mc);
static void mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11);
static void mc_init_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11, byte flags,
InstrumentExtra * ie);
static void struct10_init(Struct10 * s10, InstrumentExtra * ie);
static byte struct10_ontimer(Struct10 * s10, Struct11 * s11);
static void struct10_setup(Struct10 * s10);
static int random_nr(int a);
void mc_key_on(MidiChannel * mc, byte note, byte velocity);
static void premix_proc(void *param, int16 *buf, uint len);
public:
IMuseAdlib(SoundMixer *mixer) {
_mixer = mixer;
}
void uninit() {}
void init(IMuseInternal *eng, OSystem *syst);
void update_pris() {
}
void generate_samples(int16 *buf, int len);
void on_timer();
void set_instrument(uint slot, byte *instr);
void part_set_instrument(Part *part, Instrument * instr);
void part_key_on(Part *part, byte note, byte velocity);
void part_key_off(Part *part, byte note);
void part_set_param(Part *part, byte param, int value);
void part_changed(Part *part, uint16 what);
void part_off(Part *part);
int part_update_active(Part *part, uint16 *active);
void adjust_priorities() {
}
uint32 get_base_tempo() {
#ifdef _WIN32_WCE
return 0x1F0000 * 2; // Sampled down to 11 kHz
#else //_WIN32_WCE
return 0x1F0000; // Was: 0x1924E0;
#endif //_WIN32_WCE
}
byte get_hardware_type() {
return 1;
}
};
/* IMuseGM classes */
class IMuseGM : public IMuseDriver {
IMuseInternal *_se;
OSystem *_system;
MidiDriver *_md;
MidiChannelGM _midi_channels[9];
int16 _midi_pitchbend_last[16];
byte _midi_pitchbend_factor_last[16];
uint8 _midi_volume_last[16];
bool _midi_pedal_last[16];
byte _midi_modwheel_last[16];
byte _midi_effectlevel_last[16];
byte _midi_chorus_last[16];
int8 _midi_pan_last[16];
void midiPitchBend(byte chan, int16 pitchbend);
void midiPitchBendFactor (byte chan, byte factor);
void midiVolume(byte chan, byte volume);
void midiPedal(byte chan, bool pedal);
void midiModWheel(byte chan, byte modwheel);
void midiEffectLevel(byte chan, byte level);
void midiChorus(byte chan, byte chorus);
void midiControl0(byte chan, byte value);
void midiProgram(byte chan, byte program, bool mt32emulate);
void midiPan(byte chan, int8 pan);
void midiNoteOn(byte chan, byte note, byte velocity);
void midiNoteOff(byte chan, byte note);
void midiSilence(byte chan);
void midiInit();
public:
IMuseGM(MidiDriver *midi) { _md = midi; }
void uninit();
void init(IMuseInternal *eng, OSystem *os);
void update_pris();
void part_off(Part *part);
int part_update_active(Part *part, uint16 *active);
void on_timer() {}
void set_instrument(uint slot, byte *instr) {}
void part_set_instrument(Part *part, Instrument * instr) {}
void part_set_param(Part *part, byte param, int value) {}
void part_key_on(Part *part, byte note, byte velocity);
void part_key_off(Part *part, byte note);
void part_changed(Part *part, uint16 what);
static int midi_driver_thread(void *param);
uint32 get_base_tempo() { return 0x460000; }
byte get_hardware_type() { return 5; }
};
//*********************************
//**** IMUSE helper functions ****
//*********************************
static int clamp(int val, int min, int max)
{
if (val < min)
return min;
if (val > max)
return max;
return val;
}
static int transpose_clamp(int a, int b, int c)
{
if (b > a)
a += (b - a + 11) / 12 * 12;
if (c < a)
a -= (a - c + 11) / 12 * 12;
return a;
}
static uint32 get_delta_time(byte **s)
{
byte *d = *s, b;
uint32 time = 0;
do {
b = *d++;
time = (time << 7) | (b & 0x7F);
} while (b & 0x80);
*s = d;
return time;
}
static uint read_word(byte *a)
{
return (a[0] << 8) + a[1];
}
static void skip_midi_cmd(byte **song_ptr)
{
byte *s, code;
const byte num_skip[] = {
2, 2, 2, 2, 1, 1, 2
};
s = *song_ptr;
code = *s++;
if (code < 0x80) {
s = NULL;
} else if (code < 0xF0) {
s += num_skip[(code & 0x70) >> 4];
} else {
if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) {
s += get_delta_time(&s);
} else {
s = NULL;
}
}
*song_ptr = s;
}
static int is_note_cmd(byte **a, IsNoteCmdData * isnote)
{
byte *s = *a;
byte code;
code = *s++;
switch (code >> 4) {
case 8: /* key off */
isnote->chan = code & 0xF;
isnote->note = *s++;
isnote->vel = *s++;
*a = s;
return 1;
case 9: /* key on */
isnote->chan = code & 0xF;
isnote->note = *s++;
isnote->vel = *s++;
*a = s;
if (isnote->vel)
return 2;
return 1;
case 0xA:
case 0xB:
case 0xE:
s++;
case 0xC:
case 0xD:
s++;
break;
case 0xF:
if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) {
s += get_delta_time(&s);
break;
}
return -1;
default:
return -1;
}
*a = s;
return 0;
}
/**********************************************************************/
IMuseInternal::~IMuseInternal() {
terminate();
}
void IMuseInternal::lock()
{
_locked++;
}
void IMuseInternal::unlock()
{
_locked--;
}
byte *IMuseInternal::findTag(int sound, char *tag, int index)
{
byte *ptr = NULL;
int32 size, pos;
if (_base_sounds) {
// FIXME: This is a hack to make certain parts of Sam & Max work.
// It's a NASTY HACK because it has to specifically skip sound 1.
// For some reason (maybe a script parse bug?), sound 1 is being
// played at the very beginning (opening logo). Until this "fix",
// the sound was never found and thus never played. It SHOULDN'T
// be played.
if (!_base_sounds[sound] && (sound > 1 || g_scumm->_gameId != GID_SAMNMAX))
g_scumm->ensureResourceLoaded (rtSound, sound);
ptr = _base_sounds[sound];
}
if (ptr == NULL) {
debug(1, "IMuseInternal::findTag completely failed finding sound %d", sound);
return NULL;
}
ptr += 8;
size = READ_BE_UINT32_UNALIGNED(ptr);
ptr += 4;
pos = 0;
while (pos < size) {
if (!memcmp(ptr + pos, tag, 4) && !index--)
return ptr + pos + 8;
pos += READ_BE_UINT32_UNALIGNED(ptr + pos + 4) + 8;
}
debug(3, "IMuseInternal::findTag failed finding sound %d", sound);
return NULL;
}
bool IMuseInternal::isMT32(int sound)
{
byte *ptr = NULL;
uint32 tag;
if (_base_sounds)
ptr = _base_sounds[sound];
if (ptr == NULL)
return false;
tag = *(((uint32 *)ptr) + 1);
switch (tag) {
case MKID('ADL '):
return false;
case MKID('ROL '):
return true;
case MKID('GMD '):
return false;
case MKID('MAC '):
return true;
case MKID('SPK '):
return false;
}
return false;
}
bool IMuseInternal::isGM(int sound)
{
byte *ptr = NULL;
uint32 tag;
if (_base_sounds)
ptr = _base_sounds[sound];
if (ptr == NULL)
return false;
tag = *(((uint32 *)ptr) + 1);
switch (tag) {
case MKID('ADL '):
return false;
case MKID('ROL '):
return true; // Yeah... for our purposes, this is GM
case MKID('GMD '):
return true;
case MKID('MIDI'):
return true;
case MKID('MAC '):
return true; // I guess this one too, since it qualifies under isMT32()
case MKID('SPK '):
return false;
}
return false;
}
bool IMuseInternal::start_sound(int sound)
{
Player *player;
void *mdhd;
mdhd = findTag(sound, MDHD_TAG, 0);
if (!mdhd) {
mdhd = findTag(sound, MDPG_TAG, 0);
if (!mdhd) {
warning("SE::start_sound failed: Couldn't find sound %d", sound);
return false;
}
}
// Make sure the requested sound is not already playing.
// FIXME: This should NEVER happen, but until we get all of the
// Sam & Max iMuse issues resolved, it does happen occasionally.
// This hack is just to make playtesting tolerable.
int i;
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
if (player->_active && player->_id == sound)
return true; // break;
}
if (!i)
player = allocate_player(128);
if (!player)
return false;
player->clear();
return player->start_sound(sound);
}
Player *IMuseInternal::allocate_player(byte priority)
{
Player *player = _players, *best = NULL;
int i;
byte bestpri = 255;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
if (!player->_active)
return player;
if (player->_priority < bestpri) {
best = player;
bestpri = player->_priority;
}
}
if (bestpri < priority)
return best;
debug(1, "Denying player request");
return NULL;
}
void IMuseInternal::init_players()
{
Player *player = _players;
int i;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
player->_active = false;
player->_se = this;
}
}
void IMuseInternal::init_sustaining_notes()
{
SustainingNotes *next = NULL, *sn = _sustaining_notes;
int i;
_sustain_notes_used = NULL;
_sustain_notes_head = NULL;
for (i = ARRAYSIZE(_sustaining_notes); i != 0; i--, sn++) {
sn->next = next;
next = sn;
}
_sustain_notes_free = next;
}
void IMuseInternal::init_volume_fader()
{
VolumeFader *vf = _volume_fader;
int i;
for (i = ARRAYSIZE(_volume_fader); i != 0; i--, vf++)
vf->initialize();
_active_volume_faders = false;
}
void IMuseInternal::init_parts()
{
Part *part;
int i;
for (i = 0, part = _parts; i != ARRAYSIZE(_parts); i++, part++) {
part->init(_driver);
part->_slot = i;
}
}
int IMuseInternal::stop_sound(int sound)
{
Player *player = _players;
int i;
int r = -1;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
if (player->_active && player->_id == sound) {
player->clear();
r = 0;
}
}
return r;
}
int IMuseInternal::stop_all_sounds()
{
Player *player = _players;
int i;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
if (player->_active)
player->clear();
}
return 0;
}
void IMuseInternal::on_timer()
{
if (_locked || _paused)
return;
lock();
sequencer_timers();
expire_sustain_notes();
expire_volume_faders();
_driver->on_timer();
unlock();
}
void IMuseInternal::sequencer_timers()
{
Player *player = _players;
int i;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
if (player->_active)
player->sequencer_timer();
}
}
void IMuseInternal::handle_marker(uint id, byte data)
{
uint16 *p;
uint pos;
pos = _queue_end;
if (pos == _queue_pos)
return;
if (_queue_adding && _queue_sound == id && data == _queue_marker)
return;
p = _cmd_queue[pos].array;
if (p[0] != TRIGGER_ID || p[1] != id || p[2] != data)
return;
_trigger_count--;
_queue_cleared = false;
do {
pos = (pos + 1) & (ARRAYSIZE(_cmd_queue) - 1);
if (_queue_pos == pos)
break;
p = _cmd_queue[pos].array;
if (*p++ != COMMAND_ID)
break;
_queue_end = pos;
do_command(p[0], p[1], p[2], p[3], p[4], p[5], p[6], 0);
if (_queue_cleared)
return;
pos = _queue_end;
} while (1);
_queue_end = pos;
}
int IMuseInternal::get_channel_volume(uint a)
{
if (a < 8)
return _channel_volume_eff[a];
return _master_volume;
}
Part *IMuseInternal::allocate_part(byte pri)
{
Part *part, *best = NULL;
int i;
for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
if (!part->_player)
return part;
if (pri >= part->_pri_eff) {
pri = part->_pri_eff;
best = part;
}
}
if (best)
best->uninit();
else
debug(1, "Denying part request");
return best;
}
void IMuseInternal::expire_sustain_notes()
{
SustainingNotes *sn, *next;
Player *player;
uint32 counter;
for (sn = _sustain_notes_head; sn; sn = next) {
next = sn->next;
player = sn->player;
counter = sn->counter + player->_timer_speed;
sn->pos += counter >> 16;
sn->counter = (unsigned short)counter & 0xFFFF;
if (sn->pos >= sn->off_pos) {
player->key_off(sn->chan, sn->note);
/* Unlink the node */
if (next)
next->prev = sn->prev;
if (sn->prev)
sn->prev->next = next;
else
_sustain_notes_head = next;
/* And put it in the free list */
sn->next = _sustain_notes_free;
_sustain_notes_free = sn;
}
}
}
void IMuseInternal::expire_volume_faders()
{
VolumeFader *vf;
int i;
if (++_volume_fader_counter & 7)
return;
if (!_active_volume_faders)
return;
_active_volume_faders = false;
vf = _volume_fader;
for (i = ARRAYSIZE(_volume_fader); i != 0; i--, vf++) {
if (vf->active) {
_active_volume_faders = true;
vf->on_timer(false);
}
}
}
void VolumeFader::on_timer(bool probe)
{
byte newvol;
newvol = curvol + speed_hi;
speed_lo_counter += speed_lo;
if (speed_lo_counter >= speed_lo_max) {
speed_lo_counter -= speed_lo_max;
newvol += direction;
}
if (curvol != newvol) {
curvol = newvol;
if (!newvol) {
if (!probe)
player->clear();
active = false;
return;
}
if (!probe)
player->set_vol(newvol);
}
if (!--num_steps) {
active = false;
}
}
byte VolumeFader::fading_to()
{
byte newvol;
byte orig_curvol;
uint16 orig_speed_lo_counter, orig_num_steps;
if (!active)
return 127;
// It would be so much easier to just store the fade-to volume in a
// variable, but then we'd have to break savegame compatibility. So
// instead we do a "dry run" fade.
orig_speed_lo_counter = speed_lo_counter;
orig_num_steps = num_steps;
orig_curvol = curvol;
while (active)
on_timer(true);
active = true;
newvol = curvol;
speed_lo_counter = orig_speed_lo_counter;
num_steps = orig_num_steps;
curvol = orig_curvol;
return newvol;
}
int IMuseInternal::get_sound_status(int sound)
{
int i;
Player *player;
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
if (player->_active && player->_id == (uint16)sound) {
// Assume that anyone asking for the sound status is
// really asking "is it ok if I start playing this
// sound now?" So if the sound is about to fade out,
// shut it down and pretend it wasn't playing.
if (player->is_fading_out()) {
player->clear();
continue;
}
return 1;
}
}
return get_queue_sound_status(sound);
}
int IMuseInternal::get_queue_sound_status(int sound)
{
uint16 *a;
int i, j;
j = _queue_pos;
i = _queue_end;
while (i != j) {
a = _cmd_queue[i].array;
if (a[0] == COMMAND_ID && a[1] == 8 && a[2] == (uint16)sound)
return 2;
i = (i + 1) & (ARRAYSIZE(_cmd_queue) - 1);
}
return 0;
}
int IMuseInternal::set_volchan(int sound, int volchan)
{
int r;
int i;
int num;
Player *player, *best, *sameid;
r = get_volchan_entry(volchan);
if (r == -1)
return -1;
if (r >= 8) {
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
if (player->_active && player->_id == (uint16)sound && player->_vol_chan != (uint16)volchan) {
player->_vol_chan = volchan;
player->set_vol(player->_volume);
return 0;
}
}
return -1;
} else {
best = NULL;
num = 0;
sameid = NULL;
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
if (player->_active) {
if (player->_vol_chan == (uint16)volchan) {
num++;
if (!best || player->_priority <= best->_priority)
best = player;
} else if (player->_id == (uint16)sound) {
sameid = player;
}
}
}
if (sameid == NULL)
return -1;
if (num >= r)
best->clear();
player->_vol_chan = volchan;
player->set_vol(player->_volume);
return 0;
}
}
int IMuseInternal::clear_queue()
{
_queue_adding = false;
_queue_cleared = true;
_queue_pos = 0;
_queue_end = 0;
_trigger_count = 0;
return 0;
}
int IMuseInternal::enqueue_command(int a, int b, int c, int d, int e, int f, int g)
{
uint16 *p;
uint i;
i = _queue_pos;
if (i == _queue_end)
return -1;
if (a == -1) {
_queue_adding = false;
_trigger_count++;
return 0;
}
p = _cmd_queue[_queue_pos].array;
p[0] = COMMAND_ID;
p[1] = a;
p[2] = b;
p[3] = c;
p[4] = d;
p[5] = e;
p[6] = f;
p[7] = g;
i = (i + 1) & (ARRAYSIZE(_cmd_queue) - 1);
if (_queue_end != i) {
_queue_pos = i;
return 0;
} else {
_queue_pos = (i - 1) & (ARRAYSIZE(_cmd_queue) - 1);
return -1;
}
}
int IMuseInternal::query_queue(int param)
{
switch (param) {
case 0: /* get trigger count */
return _trigger_count;
case 1: /* get trigger type */
if (_queue_end == _queue_pos)
return -1;
return _cmd_queue[_queue_end].array[1];
case 2: /* get trigger sound */
if (_queue_end == _queue_pos)
return 0xFF;
return _cmd_queue[_queue_end].array[2];
default:
return -1;
}
}
int IMuseInternal::get_music_volume()
{
return _music_volume * 2;
}
int IMuseInternal::set_music_volume(uint vol)
{
if (vol > 256)
vol = 256;
else if (vol < 0)
vol = 0;
_music_volume = vol / 2;
return 0;
}
int IMuseInternal::set_master_volume_intern(uint vol)
{
if (vol > 127)
return -1;
vol = vol * _music_volume / 128;
_master_volume = vol;
for (int i = 0; i != 8; i++)
_channel_volume_eff[i] = (_channel_volume[i] + 1) * vol >> 7;
update_volumes();
return 0;
}
int IMuseInternal::set_master_volume(uint vol)
{
// recalibrate from 0-256 range
vol = vol * 127 / 256;
return set_master_volume_intern(vol);
}
int IMuseInternal::get_master_volume()
{
// recalibrate to 0-256 range
return _master_volume * 256 / 127;
}
int IMuseInternal::terminate()
{
if (_driver) {
_driver->uninit();
delete _driver;
_driver = NULL;
}
return 0;
/* not implemented */
}
int IMuseInternal::enqueue_trigger(int sound, int marker)
{
uint16 *p;
uint pos;
pos = _queue_pos;
p = _cmd_queue[pos].array;
p[0] = TRIGGER_ID;
p[1] = sound;
p[2] = marker;
pos = (pos + 1) & (ARRAYSIZE(_cmd_queue) - 1);
if (_queue_end == pos) {
_queue_pos = (pos - 1) & (ARRAYSIZE(_cmd_queue) - 1);
return -1;
}
_queue_pos = pos;
_queue_adding = true;
_queue_sound = sound;
_queue_marker = marker;
return 0;
}
int32 IMuseInternal::do_command(int a, int b, int c, int d, int e, int f, int g, int h)
{
int i;
byte cmd = a & 0xFF;
byte param = a >> 8;
Player *player = NULL;
if (!_initialized && (cmd || param))
return -1;
if (param == 0) {
switch (cmd) {
case 6:
return set_master_volume_intern(b);
case 7:
return _master_volume;
case 8:
return start_sound(b) ? 0 : -1;
case 9:
return stop_sound(b);
case 10: // FIXME: Sam and Max - Not sure if this is correct
return stop_all_sounds();
case 11:
return stop_all_sounds();
case 12:
// Sam & Max: Player-scope commands
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
if (player->_active && player->_id == (uint16)b)
break;
}
if (!i)
return -1;
switch (d) {
case 6:
// Set player volume.
// Jamieson360: Good, this is consistent with IMuseDigital.
return player->set_vol (e);
default:
warning("IMuseInternal::do_command (6) unsupported sub-command %d", d);
}
return -1;
case 13:
return get_sound_status(b);
case 14:
// Sam and Max: Volume Fader?
// Prevent instantaneous volume fades.
// Fixes a Ball of Twine issue, but might not be the right long-term solution.
if (f != 0) {
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
if (player->_active && player->_id == (uint16)b) {
player->fade_vol(e, f);
return 0;
}
}
}
return -1;
case 15:
// Sam & Max: Unconditional Jump?
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
if (player->_active && player->_id == (uint16)b) {
player->_hook._jump = d;
return 0;
}
}
return -1;
case 16:
return set_volchan(b, c);
case 17:
if (g_scumm->_gameId != GID_SAMNMAX) {
return set_channel_volume(b, c);
} else {
// Sam & Max: ImSetTrigger.
// Sets a trigger for a particular player and
// marker ID, along with do_command parameters
// to invoke at the marker. The marker is
// represented by MIDI SysEx block 00 xx (F7)
// where "xx" is the marker ID.
for (i = 0; i < 16; ++i) {
if (!_snm_triggers [i].id) {
_snm_triggers [i].id = d;
_snm_triggers [i].sound = b;
_snm_triggers [i].command [0] = e;
_snm_triggers [i].command [1] = f;
_snm_triggers [i].command [2] = g;
_snm_triggers [i].command [3] = h;
return 0;
}
}
return -1;
}
case 18:
if (g_scumm->_gameId != GID_SAMNMAX) {
return set_volchan_entry(b, c);
} else {
// Sam & Max: ImCheckTrigger.
// According to Mike's notes to Ender,
// this function returns the number of triggers
// associated with a particular player ID and
// trigger ID.
a = 0;
for (i = 0; i < 16; ++i) {
if (_snm_triggers [i].sound == b && _snm_triggers [i].id &&
(d == -1 || _snm_triggers [i].id == d))
{
++a;
}
}
return a;
}
case 19:
// Sam & Max: ImClearTrigger
// This should clear a trigger that's been set up
// with ImSetTrigger (cmd == 17). Seems to work....
a = 0;
for (i = 0; i < 16; ++i) {
if (_snm_triggers [i].sound == b && _snm_triggers [i].id &&
(d == -1 || _snm_triggers [i].id == d))
{
_snm_triggers [i].sound = _snm_triggers [i].id = 0;
++a;
}
}
return (a > 0) ? 0 : -1;
case 20: // FIXME: Deferred command system? - Sam and Max
return 0;
case 2:
case 3:
return 0;
default:
warning("IMuseInternal::do_command invalid command %d", cmd);
}
} else if (param == 1) {
if ((1 << cmd) & (0x783FFF)) {
player = get_player_byid(b);
if (!player)
return -1;
if ((1 << cmd) & (1 << 11 | 1 << 22)) {
assert(c >= 0 && c <= 15);
player = (Player *)player->get_part(c);
if (!player)
return -1;
}
}
switch (cmd) {
case 0:
if (g_scumm->_gameId == GID_SAMNMAX)
return player->_marker;
else
return player->get_param(c, d);
case 1:
if (g_scumm->_gameId == GID_SAMNMAX)
player->jump (d - 1, (e - 1) * 4 + f, ((g * player->_ticks_per_beat) >> 2) + h);
else
player->set_priority(c);
return 0;
case 2:
return player->set_vol(c);
case 3:
player->set_pan(c);
return 0;
case 4:
return player->set_transpose(c, d);
case 5:
player->set_detune(c);
return 0;
case 6:
player->set_speed(c);
return 0;
case 7:
return player->jump(c, d, e) ? 0 : -1;
case 8:
return player->scan(c, d, e);
case 9:
return player->set_loop(c, d, e, f, g) ? 0 : -1;
case 10:
player->clear_loop();
return 0;
case 11:
((Part *)player)->set_onoff(d != 0);
return 0;
case 12:
return player->_hook.set(c, d, e);
case 13:
return player->fade_vol(c, d);
case 14:
return enqueue_trigger(b, c);
case 15:
return enqueue_command(b, c, d, e, f, g, h);
case 16:
return clear_queue();
case 19:
return player->get_param(c, d);
case 20:
return player->_hook.set(c, d, e);
case 21:
return -1;
case 22:
((Part *)player)->set_vol(d);
return 0;
case 23:
return query_queue(b);
case 24:
return 0;
default:
warning("IMuseInternal::do_command default midi command %d", cmd);
return -1;
}
}
return -1;
}
int IMuseInternal::set_channel_volume(uint chan, uint vol)
{
if (chan >= 8 || vol > 127)
return -1;
_channel_volume[chan] = vol;
_channel_volume_eff[chan] = _master_volume * (vol + 1) >> 7;
update_volumes();
return 0;
}
void IMuseInternal::update_volumes()
{
Player *player;
int i;
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
if (player->_active)
player->set_vol(player->_volume);
}
}
int IMuseInternal::set_volchan_entry(uint a, uint b)
{
if (a >= 8)
return -1;
_volchan_table[a] = b;
return 0;
}
int HookDatas::query_param(int param, byte chan)
{
switch (param) {
case 18:
return _jump;
case 19:
return _transpose;
case 20:
return _part_onoff[chan];
case 21:
return _part_volume[chan];
case 22:
return _part_program[chan];
case 23:
return _part_transpose[chan];
default:
return -1;
}
}
int HookDatas::set(byte cls, byte value, byte chan)
{
switch (cls) {
case 0:
_jump = value;
break;
case 1:
_transpose = value;
break;
case 2:
if (chan < 16)
_part_onoff[chan] = value;
else if (chan == 16)
memset(_part_onoff, value, 16);
break;
case 3:
if (chan < 16)
_part_volume[chan] = value;
else if (chan == 16)
memset(_part_volume, value, 16);
break;
case 4:
if (chan < 16)
_part_program[chan] = value;
else if (chan == 16)
memset(_part_program, value, 16);
break;
case 5:
if (chan < 16)
_part_transpose[chan] = value;
else if (chan == 16)
memset(_part_transpose, value, 16);
break;
default:
return -1;
}
return 0;
}
VolumeFader *IMuseInternal::allocate_volume_fader()
{
VolumeFader *vf;
int i;
vf = _volume_fader;
for (i = ARRAYSIZE(_volume_fader); vf->active;) {
vf++;
if (!--i)
return NULL;
}
vf->active = true;
_active_volume_faders = true;
return vf;
}
Player *IMuseInternal::get_player_byid(int id)
{
int i;
Player *player, *found = NULL;
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
if (player->_active && player->_id == (uint16)id) {
if (found)
return NULL;
found = player;
}
}
return found;
}
int IMuseInternal::get_volchan_entry(uint a)
{
if (a < 8)
return _volchan_table[a];
return -1;
}
uint32 IMuseInternal::property(int prop, uint32 value)
{
switch (prop) {
case IMuse::PROP_TEMPO_BASE:
_game_tempo = value;
break;
}
return 0;
}
void IMuseInternal::setBase(byte **base)
{
_base_sounds = base;
}
IMuseInternal *IMuseInternal::create(OSystem *syst, MidiDriver *midi, SoundMixer *mixer)
{
IMuseInternal *i = new IMuseInternal;
i->initialize(syst, midi, mixer);
return i;
}
int IMuseInternal::initialize(OSystem *syst, MidiDriver *midi, SoundMixer *mixer)
{
int i;
IMuseDriver *driv;
if (midi == NULL) {
driv = new IMuseAdlib(mixer);
} else {
driv = new IMuseGM(midi);
}
_driver = driv;
_hardware_type = driv->get_hardware_type();
_game_tempo = driv->get_base_tempo();
driv->init(this, syst);
_master_volume = 127;
if (_music_volume < 1)
_music_volume = kDefaultMusicVolume;
for (i = 0; i != 8; i++)
_channel_volume[i] = _channel_volume_eff[i] = _volchan_table[i] = 127;
init_players();
init_sustaining_notes();
init_volume_fader();
init_queue();
init_parts();
_initialized = true;
return 0;
}
void IMuseInternal::init_queue()
{
_queue_adding = false;
_queue_pos = 0;
_queue_end = 0;
_trigger_count = 0;
}
void IMuseInternal::pause(bool paused)
{
lock();
int i;
Part *part;
for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
if (part->_player) {
if (paused) {
part->_vol_eff = 0;
} else {
part->set_vol(part->_vol);
}
part->changed(IMuseDriver::pcVolume);
}
}
_paused = paused;
unlock();
}
/*************************************************************************/
int Player::fade_vol(byte vol, int time)
{
VolumeFader *vf;
int i;
cancel_volume_fade();
if (time == 0) {
set_vol(vol);
return 0;
}
vf = _se->allocate_volume_fader();
if (vf == NULL)
return -1;
vf->player = this;
vf->num_steps = vf->speed_lo_max = time;
vf->curvol = _volume;
i = (vol - vf->curvol);
vf->speed_hi = i / time;
if (i < 0) {
i = -i;
vf->direction = -1;
} else {
vf->direction = 1;
}
vf->speed_lo = i % time;
vf->speed_lo_counter = 0;
return 0;
}
bool Player::is_fading_out()
{
VolumeFader *vf = _se->_volume_fader;
int i;
for (i = 0; i < 8; i++, vf++) {
if (vf->active && vf->direction < 0 && vf->player == this && vf->fading_to() == 0)
return true;
}
return false;
}
void Player::clear()
{
uninit_seq();
cancel_volume_fade();
uninit_parts();
_active = false;
_ticks_per_beat = TICKS_PER_BEAT;
}
bool Player::start_sound(int sound)
{
void *mdhd;
mdhd = _se->findTag(sound, MDHD_TAG, 0);
if (mdhd == NULL) {
mdhd = _se->findTag(sound, MDPG_TAG, 0);
if (mdhd == NULL) {
warning("P::start_sound failed: Couldn't find %s", MDHD_TAG);
return false;
}
}
_mt32emulate = _se->isMT32(sound);
_isGM = _se->isGM(sound);
_parts = NULL;
_active = true;
_id = sound;
_priority = 0x80;
_volume = 0x7F;
_vol_chan = 0xFFFF;
_vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;
_pan = 0;
_transpose = 0;
_detune = 0;
hook_clear();
if (start_seq_sound(sound) != 0) {
_active = false;
return false;
}
return true;
}
void Player::hook_clear()
{
memset(&_hook, 0, sizeof(_hook));
}
int Player::start_seq_sound(int sound)
{
byte *ptr, *track_ptr;
_song_index = sound;
_timer_counter = 0;
_loop_to_beat = 1;
_loop_from_beat = 1;
_track_index = 0;
_loop_counter = 0;
_loop_to_tick = 0;
_loop_from_tick = 0;
set_tempo(500000);
set_speed(128);
ptr = _se->findTag(sound, "MTrk", _track_index);
if (ptr == NULL)
return -1;
track_ptr = ptr;
_cur_pos = _next_pos = get_delta_time(&track_ptr);
_song_offset = track_ptr - ptr;
_tick_index = _cur_pos;
_beat_index = 1;
if (_tick_index >= _ticks_per_beat) {
_beat_index += _tick_index / _ticks_per_beat;
_tick_index %= _ticks_per_beat;
}
return 0;
}
void Player::set_tempo(uint32 b)
{
uint32 i, j;
i = _se->_game_tempo;
j = _tempo = b;
while (i & 0xFFFF0000 || j & 0xFFFF0000) {
i >>= 1;
j >>= 1;
}
_tempo_eff = (i << 16) / j;
set_speed(_speed);
}
void Player::cancel_volume_fade()
{
VolumeFader *vf = _se->_volume_fader;
int i;
for (i = 0; i < 8; i++, vf++) {
if (vf->active && vf->player == this)
vf->active = false;
}
}
void Player::uninit_parts()
{
if (_parts && _parts->_player != this)
error("asd");
while (_parts)
_parts->uninit();
}
void Player::uninit_seq()
{
_abort = true;
}
void Player::set_speed(byte speed)
{
_speed = speed;
_timer_speed = (_tempo_eff * speed >> 7);
}
byte *Player::parse_midi(byte *s)
{
byte cmd, chan, note, velocity, control;
uint value;
Part *part;
cmd = *s++;
chan = cmd & 0xF;
switch (cmd >> 4) {
case 0x8: /* key off */
note = *s++;
if (!_scanning) {
key_off(chan, note);
} else {
clear_active_note(chan, note);
}
s++; /* skip velocity */
break;
case 0x9: /* key on */
note = *s++;
velocity = *s++;
if (velocity) {
if (!_scanning)
key_on(chan, note, velocity);
else
set_active_note(chan, note);
} else {
if (!_scanning)
key_off(chan, note);
else
clear_active_note(chan, note);
}
break;
case 0xA: /* aftertouch */
s += 2;
break;
case 0xB: /* control change */
control = *s++;
value = *s++;
part = get_part(chan);
if (!part)
break;
switch (control) {
case 1: /* modulation wheel */
part->set_modwheel(value);
break;
case 7: /* volume */
part->set_vol(value);
break;
case 10: /* pan position */
part->set_pan(value - 0x40);
break;
case 16: /* pitchbend factor */
part->set_pitchbend_factor(value);
break;
case 17: /* gp slider 2 */
part->set_detune(value - 0x40);
break;
case 18: /* gp slider 3 */
part->set_pri(value - 0x40);
_se->_driver->update_pris();
break;
case 64: /* hold pedal */
part->set_pedal(value != 0);
break;
case 91: /* effects level */
part->set_effect_level(value);
break;
case 93: /* chorus */
part->set_chorus(value);
break;
default:
warning("parse_midi: invalid control %d", control);
}
break;
case 0xC: /* program change */
value = *s++;
debug (2, "Player::parse_midi - Setting channel %2d to program %d", chan, value); // Jamieson630: Helps to build the GM-to-FM mapping
part = get_part(chan);
if (part)
part->set_program(value);
break;
case 0xD: /* channel pressure */
s++;
break;
case 0xE: /* pitch bend */
part = get_part(chan);
if (part)
// part->set_pitchbend(((s[1] - 0x40) << 7) | s[0]);
part->set_pitchbend(((s[1] << 7) | s[0]) - 0x2000);
s += 2;
break;
case 0xF:
if (chan == 0) {
uint size = get_delta_time(&s);
if (*s == SYSEX_ID)
parse_sysex(s, size);
s += size;
} else if (chan == 0xF) {
cmd = *s++;
if (cmd == 47)
goto Error; /* end of song */
if (cmd == 81) {
set_tempo((s[1] << 16) | (s[2] << 8) | s[3]);
s += 4;
break;
}
s += get_delta_time(&s);
} else if (chan == 0x7) {
s += get_delta_time(&s);
} else {
goto Error;
}
break;
default:
Error:;
if (!_scanning)
clear();
return NULL;
}
return s;
}
void Player::parse_sysex(byte *p, uint len)
{
byte code;
byte a;
uint b;
byte buf[128];
Part *part;
/* too big? */
if (len >= sizeof(buf) * 2)
return;
/* skip sysex manufacturer */
p++;
len -= 2;
switch (code = *p++) {
case 0:
if (g_scumm->_gameId != GID_SAMNMAX) {
// Part on/off?
// This seems to do the right thing for Monkey 2, at least.
a = *p++ & 0x0F;
part = get_part(a);
if (part) {
debug(2, "%d => turning %s part %d", p[1], (p[1] == 2) ? "OFF" : "ON", a);
part->set_onoff(p[1] != 2);
}
} else {
// Jamieson630: Sam & Max seems to use this as a marker for
// ImSetTrigger. When a marker is encountered whose sound
// ID and (presumably) marker ID match what was set by
// ImSetTrigger, something magical is supposed to happen....
_marker = *p;
for (a = 0; a < 16; ++a) {
if (_se->_snm_triggers [a].sound == _id &&
_se->_snm_triggers [a].id == *p)
{
_se->_snm_triggers [a].sound = _se->_snm_triggers [a].id = 0;
_se->do_command (_se->_snm_triggers [a].command [0],
_se->_snm_triggers [a].command [1],
_se->_snm_triggers [a].command [2],
_se->_snm_triggers [a].command [3],
0, 0, 0, 0);
break;
}
}
} // end if
break;
case 1:
// This SysEx is used in Sam & Max for maybe_jump.
if (_scanning)
break;
maybe_jump (p[0], p[1] - 1, (read_word (p + 2) - 1) * 4 + p[4], ((p[5] * _ticks_per_beat) >> 2) + p[6]);
break;
case 16: /* set instrument in part */
a = *p++ & 0x0F;
if (_se->_hardware_type != *p++)
break;
decode_sysex_bytes(p, buf, len - 3);
part = get_part(a);
if (part)
part->set_instrument((Instrument *) buf);
break;
case 17: /* set global instrument */
p++;
if (_se->_hardware_type != *p++)
break;
a = *p++;
decode_sysex_bytes(p, buf, len - 4);
_se->_driver->set_instrument(a, buf);
break;
case 33: /* param adjust */
a = *p++ & 0x0F;
if (_se->_hardware_type != *p++)
break;
decode_sysex_bytes(p, buf, len - 3);
part = get_part(a);
if (part)
part->set_param(read_word(buf), read_word(buf + 2));
break;
case 48: /* hook - jump */
if (_scanning)
break;
decode_sysex_bytes(p + 1, buf, len - 2);
maybe_jump (buf[0], read_word (buf + 1), read_word (buf + 3), read_word (buf + 5));
break;
case 49: /* hook - global transpose */
decode_sysex_bytes(p + 1, buf, len - 2);
maybe_set_transpose(buf);
break;
case 50: /* hook - part on/off */
buf[0] = *p++ & 0x0F;
decode_sysex_bytes(p, buf + 1, len - 2);
maybe_part_onoff(buf);
break;
case 51: /* hook - set volume */
buf[0] = *p++ & 0x0F;
decode_sysex_bytes(p, buf + 1, len - 2);
maybe_set_volume(buf);
break;
case 52: /* hook - set program */
buf[0] = *p++ & 0x0F;
decode_sysex_bytes(p, buf + 1, len - 2);
maybe_set_program(buf);
break;
case 53: /* hook - set transpose */
buf[0] = *p++ & 0x0F;
decode_sysex_bytes(p, buf + 1, len - 2);
maybe_set_transpose_part(buf);
break;
case 64: /* marker */
p++;
len -= 2;
while (len--) {
_se->handle_marker(_id, *p++);
}
break;
case 80: /* loop */
decode_sysex_bytes(p + 1, buf, len - 2);
set_loop(read_word(buf),
read_word(buf + 2), read_word(buf + 4), read_word(buf + 6), read_word(buf + 8)
);
break;
case 81: /* end loop */
clear_loop();
break;
case 96: /* set instrument */
part = get_part(p[0] & 0x0F);
b = (p[1] & 0x0F) << 12 | (p[2] & 0x0F) << 8 | (p[4] & 0x0F) << 4 | (p[4] & 0x0F);
if (part)
part->set_instrument(b);
break;
default:
debug(6, "unknown sysex %d", code);
}
}
void Player::decode_sysex_bytes(byte *src, byte *dst, int len)
{
while (len >= 0) {
*dst++ = (src[0] << 4) | (src[1] & 0xF);
src += 2;
len -= 2;
}
}
void Player::maybe_jump (byte cmd, uint track, uint beat, uint tick)
{
/* is this the hook i'm waiting for? */
if (cmd && _hook._jump != cmd)
return;
/* reset hook? */
if (cmd != 0 && cmd < 0x80)
_hook._jump = 0;
jump (track, beat, tick);
}
void Player::maybe_set_transpose(byte *data)
{
byte cmd;
cmd = data[0];
/* is this the hook i'm waiting for? */
if (cmd && _hook._transpose != cmd)
return;
/* reset hook? */
if (cmd != 0 && cmd < 0x80)
_hook._transpose = 0;
set_transpose(data[1], (int8)data[2]);
}
void Player::maybe_part_onoff(byte *data)
{
byte cmd, *p;
uint chan;
Part *part;
cmd = data[1];
chan = data[0];
p = &_hook._part_onoff[chan];
/* is this the hook i'm waiting for? */
if (cmd && *p != cmd)
return;
if (cmd != 0 && cmd < 0x80)
*p = 0;
part = get_part(chan);
if (part)
part->set_onoff(data[2] != 0);
}
void Player::maybe_set_volume(byte *data)
{
byte cmd;
byte *p;
uint chan;
Part *part;
cmd = data[1];
chan = data[0];
p = &_hook._part_volume[chan];
/* is this the hook i'm waiting for? */
if (cmd && *p != cmd)
return;
/* reset hook? */
if (cmd != 0 && cmd < 0x80)
*p = 0;
part = get_part(chan);
if (part)
part->set_vol(data[2]);
}
void Player::maybe_set_program(byte *data)
{
byte cmd;
byte *p;
uint chan;
Part *part;
cmd = data[1];
chan = data[0];
/* is this the hook i'm waiting for? */
p = &_hook._part_program[chan];
if (cmd && *p != cmd)
return;
if (cmd != 0 && cmd < 0x80)
*p = 0;
part = get_part(chan);
if (part)
part->set_program(data[2]);
}
void Player::maybe_set_transpose_part(byte *data)
{
byte cmd;
byte *p;
uint chan;
cmd = data[1];
chan = data[0];
/* is this the hook i'm waiting for? */
p = &_hook._part_transpose[chan];
if (cmd && *p != cmd)
return;
/* reset hook? */
if (cmd != 0 && cmd < 0x80)
*p = 0;
part_set_transpose(chan, data[2], (int8)data[3]);
}
int Player::set_transpose(byte relative, int b)
{
Part *part;
if (b > 24 || b < -24 || relative > 1)
return -1;
if (relative)
b = transpose_clamp(_transpose + b, -7, 7);
_transpose = b;
for (part = _parts; part; part = part->_next) {
part->set_transpose(part->_transpose);
}
return 0;
}
void Player::clear_active_notes()
{
memset(_se->_active_notes, 0, sizeof(_se->_active_notes));
}
void Player::clear_active_note(int chan, byte note)
{
_se->_active_notes[note] &= ~(1 << chan);
}
void Player::set_active_note(int chan, byte note)
{
_se->_active_notes[note] |= (1 << chan);
}
void Player::part_set_transpose(uint8 chan, byte relative, int8 b)
{
Part *part;
if (b > 24 || b < -24)
return;
part = get_part(chan);
if (!part)
return;
if (relative)
b = transpose_clamp(b + part->_transpose, -7, 7);
part->set_transpose(b);
}
void Player::key_on(uint8 chan, uint8 note, uint8 velocity)
{
Part *part;
part = get_part(chan);
if (!part || !part->_on)
return;
part->key_on(note, velocity);
}
void Player::key_off(uint8 chan, uint8 note)
{
Part *part;
for (part = _parts; part; part = part->_next) {
if (part->_chan == (byte)chan && part->_on)
part->key_off(note);
}
}
bool Player::jump(uint track, uint beat, uint tick)
{
byte *mtrk, *cur_mtrk, *scanpos;
uint32 topos, curpos, track_offs;
if (!_active)
return false;
mtrk = _se->findTag(_song_index, "MTrk", track);
if (!mtrk)
return false;
cur_mtrk = _se->findTag(_song_index, "MTrk", _track_index);
if (!cur_mtrk)
return false;
_se->lock();
if (beat == 0)
beat = 1;
topos = (beat - 1) * _ticks_per_beat + tick;
if (track == _track_index && topos >= _cur_pos) {
scanpos = _song_offset + mtrk;
curpos = _next_pos;
} else {
scanpos = mtrk;
curpos = get_delta_time(&scanpos);
}
while (curpos < topos) {
skip_midi_cmd(&scanpos);
if (!scanpos) {
_se->unlock();
return false;
}
curpos += get_delta_time(&scanpos);
}
track_offs = scanpos - mtrk;
turn_off_pedals();
find_sustaining_notes(cur_mtrk + _song_offset, mtrk + track_offs, curpos - topos);
_beat_index = beat;
_tick_index = tick;
_cur_pos = topos;
_next_pos = curpos;
_timer_counter = 0;
_song_offset = track_offs;
if (track != _track_index) {
_track_index = track;
_loop_counter = 0;
}
_abort = true;
_se->unlock();
return true;
}
bool Player::set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick)
{
if (tobeat + 1 >= frombeat)
return false;
if (tobeat == 0)
tobeat = 1;
_loop_counter = 0; /* because of possible interrupts */
_loop_to_beat = tobeat;
_loop_to_tick = totick;
_loop_from_beat = frombeat;
_loop_from_tick = fromtick;
_loop_counter = count;
return true;
}
void Player::clear_loop()
{
_loop_counter = 0;
}
void Player::turn_off_pedals()
{
Part *part;
for (part = _parts; part; part = part->_next) {
if (part->_pedal)
part->set_pedal(false);
}
}
void Player::find_sustaining_notes(byte *a, byte *b, uint32 l)
{
uint32 pos;
uint16 mask;
uint16 *bitlist_ptr;
SustainingNotes *sn, *next;
IsNoteCmdData isnote;
int j;
uint num_active;
uint max_off_pos;
num_active = update_actives();
/* pos contains number of ticks since current position */
pos = _next_pos - _cur_pos;
if ((int32)pos < 0)
pos = 0;
/* locate the positions where the notes are turned off.
* remember each note that was turned off
*/
while (num_active != 0) {
/* is note off? */
j = is_note_cmd(&a, &isnote);
if (j == -1)
break;
if (j == 1) {
mask = 1 << isnote.chan;
bitlist_ptr = _se->_active_notes + isnote.note;
if (*bitlist_ptr & mask) {
*bitlist_ptr &= ~mask;
num_active--;
/* Get a node from the free list */
if ((sn = _se->_sustain_notes_free) == NULL)
return;
_se->_sustain_notes_free = sn->next;
/* Insert it in the beginning of the used list */
sn->next = _se->_sustain_notes_used;
_se->_sustain_notes_used = sn;
sn->prev = NULL;
if (sn->next)
sn->next->prev = sn;
sn->note = isnote.note;
sn->chan = isnote.chan;
sn->player = this;
sn->off_pos = pos;
sn->pos = 0;
sn->counter = 0;
}
}
pos += get_delta_time(&a);
}
/* find the maximum position where a note was turned off */
max_off_pos = 0;
for (sn = _se->_sustain_notes_used; sn; sn = sn->next) {
_se->_active_notes[sn->note] |= (1 << sn->chan);
if (sn->off_pos > max_off_pos) {
max_off_pos = sn->off_pos;
}
}
/* locate positions where notes are turned on */
pos = l;
while (pos < max_off_pos) {
j = is_note_cmd(&b, &isnote);
if (j == -1)
break;
if (j == 2) {
mask = 1 << isnote.chan;
bitlist_ptr = _se->_active_notes + isnote.note;
if (*bitlist_ptr & mask) {
sn = _se->_sustain_notes_used;
while (sn) {
next = sn->next;
if (sn->note == isnote.note && sn->chan == isnote.chan && pos < sn->off_pos) {
*bitlist_ptr &= ~mask;
/* Unlink from the sustain list */
if (next)
next->prev = sn->prev;
if (sn->prev)
sn->prev->next = next;
else
_se->_sustain_notes_used = next;
/* Insert into the free list */
sn->next = _se->_sustain_notes_free;
_se->_sustain_notes_free = sn;
}
sn = next;
}
}
}
pos += get_delta_time(&b);
}
/* Concatenate head and used list */
if (!_se->_sustain_notes_head) {
_se->_sustain_notes_head = _se->_sustain_notes_used;
_se->_sustain_notes_used = NULL;
return;
}
sn = _se->_sustain_notes_head;
while (sn->next)
sn = sn->next;
sn->next = _se->_sustain_notes_used;
_se->_sustain_notes_used = NULL;
if (sn->next)
sn->next->prev = sn;
}
Part *Player::get_part(uint8 chan)
{
Part *part;
part = _parts;
while (part) {
if (part->_chan == chan)
return part;
part = part->_next;
}
part = _se->allocate_part(_priority);
if (!part) {
warning("no parts available");
return NULL;
}
part->_chan = chan;
part->setup(this);
return part;
}
uint Player::update_actives()
{
Part *part;
uint16 *active;
int count = 0;
clear_active_notes();
active = _se->_active_notes;
for (part = _parts; part; part = part->_next) {
if (part->_mc)
count += part->update_actives(active);
}
return count;
}
void Player::set_priority(int pri)
{
Part *part;
_priority = pri;
for (part = _parts; part; part = part->_next) {
part->set_pri(part->_pri);
}
_se->_driver->update_pris();
}
void Player::set_pan(int pan)
{
Part *part;
_pan = pan;
for (part = _parts; part; part = part->_next) {
part->set_pan(part->_pan);
}
}
void Player::set_detune(int detune)
{
Part *part;
_detune = detune;
for (part = _parts; part; part = part->_next) {
part->set_detune(part->_detune);
}
}
int Player::scan(uint totrack, uint tobeat, uint totick)
{
byte *mtrk, *scanptr;
uint32 curpos, topos;
uint32 pos;
assert(totrack >= 0 && tobeat >= 0 && totick >= 0);
if (!_active)
return -1;
mtrk = _se->findTag(_song_index, "MTrk", totrack);
if (!mtrk)
return -1;
_se->lock();
if (tobeat == 0)
tobeat++;
turn_off_parts();
clear_active_notes();
scanptr = mtrk;
curpos = get_delta_time(&scanptr);
_scanning = true;
topos = (tobeat - 1) * _ticks_per_beat + totick;
while (curpos < topos) {
scanptr = parse_midi(scanptr);
if (!scanptr) {
_scanning = false;
_se->unlock();
return -1;
}
curpos += get_delta_time(&scanptr);
}
pos = scanptr - mtrk;
_scanning = false;
_se->driver()->update_pris();
play_active_notes();
_beat_index = tobeat;
_tick_index = totick;
_cur_pos = topos;
_next_pos = curpos;
_timer_counter = 0;
_song_offset = pos;
if (_track_index != totrack) {
_track_index = totrack;
_loop_counter = 0;
}
_se->unlock();
return 0;
}
void Player::turn_off_parts()
{
Part *part;
for (part = _parts; part; part = part->_next)
part->off();
}
void Player::play_active_notes()
{
int i, j;
uint mask;
for (i = 0; i != 128; i++) {
mask = _se->_active_notes[i];
for (j = 0; j != 16; j++, mask >>= 1) {
if (mask & 1) {
key_on(j, i, 80);
}
}
}
}
int Player::set_vol(byte vol)
{
Part *part;
if (vol > 127)
return -1;
_volume = vol;
_vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7;
for (part = _parts; part; part = part->_next) {
part->set_vol(part->_vol);
}
return 0;
}
int Player::get_param(int param, byte chan)
{
switch (param) {
case 0:
return (byte)_priority;
case 1:
return (byte)_volume;
case 2:
return (byte)_pan;
case 3:
return (byte)_transpose;
case 4:
return (byte)_detune;
case 5:
return _speed;
case 6:
return _track_index;
case 7:
return _beat_index;
case 8:
return _tick_index;
case 9:
return _loop_counter;
case 10:
return _loop_to_beat;
case 11:
return _loop_to_tick;
case 12:
return _loop_from_beat;
case 13:
return _loop_from_tick;
case 14:
case 15:
case 16:
case 17:
return query_part_param(param, chan);
case 18:
case 19:
case 20:
case 21:
case 22:
case 23:
return _hook.query_param(param, chan);
default:
return -1;
}
}
int Player::query_part_param(int param, byte chan)
{
Part *part;
part = _parts;
while (part) {
if (part->_chan == chan) {
switch (param) {
case 14:
return part->_on;
case 15:
return part->_vol;
case 16:
return part->_program;
case 17:
return part->_transpose;
default:
return -1;
}
}
part = part->_next;
}
return 129;
}
void Player::sequencer_timer()
{
byte *mtrk;
uint32 counter;
byte *song_ptr;
counter = _timer_counter + _timer_speed;
_timer_counter = counter & 0xFFFF;
_cur_pos += counter >> 16;
_tick_index += counter >> 16;
if (_tick_index >= _ticks_per_beat) {
_beat_index += _tick_index / _ticks_per_beat;
_tick_index %= _ticks_per_beat;
}
if (_loop_counter && _beat_index >= _loop_from_beat && _tick_index >= _loop_from_tick) {
_loop_counter--;
jump(_track_index, _loop_to_beat, _loop_to_tick);
}
if (_next_pos <= _cur_pos) {
mtrk = _se->findTag(_song_index, "MTrk", _track_index);
if (!mtrk) {
warning("Sound %d was unloaded while active", _song_index);
clear();
} else {
song_ptr = mtrk + _song_offset;
_abort = false;
while (_next_pos <= _cur_pos) {
song_ptr = parse_midi(song_ptr);
if (!song_ptr || _abort)
return;
_next_pos += get_delta_time(&song_ptr);
}
_song_offset = song_ptr - mtrk;
}
}
}
/*******************************************************************/
enum {
TYPE_PART = 1,
TYPE_PLAYER = 2,
};
int IMuseInternal::saveReference(IMuseInternal *me, byte type, void *ref)
{
switch (type) {
case TYPE_PART:
return (Part *)ref - me->_parts;
case TYPE_PLAYER:
return (Player *)ref - me->_players;
default:
error("saveReference: invalid type");
}
}
void *IMuseInternal::loadReference(IMuseInternal *me, byte type, int ref)
{
switch (type) {
case TYPE_PART:
return &me->_parts[ref];
case TYPE_PLAYER:
return &me->_players[ref];
default:
error("loadReference: invalid type");
}
}
int IMuseInternal::save_or_load(Serializer *ser, Scumm *scumm)
{
const SaveLoadEntry mainEntries[] = {
MKLINE(IMuseInternal, _queue_end, sleUint8),
MKLINE(IMuseInternal, _queue_pos, sleUint8),
MKLINE(IMuseInternal, _queue_sound, sleUint16),
MKLINE(IMuseInternal, _queue_adding, sleByte),
MKLINE(IMuseInternal, _queue_marker, sleByte),
MKLINE(IMuseInternal, _queue_cleared, sleByte),
MKLINE(IMuseInternal, _master_volume, sleByte),
MKLINE(IMuseInternal, _trigger_count, sleUint16),
MKARRAY(IMuseInternal, _channel_volume[0], sleUint16, 8),
MKARRAY(IMuseInternal, _volchan_table[0], sleUint16, 8),
MKEND()
};
const SaveLoadEntry playerEntries[] = {
MKREF(Player, _parts, TYPE_PART),
MKLINE(Player, _active, sleByte),
MKLINE(Player, _id, sleUint16),
MKLINE(Player, _priority, sleByte),
MKLINE(Player, _volume, sleByte),
MKLINE(Player, _pan, sleInt8),
MKLINE(Player, _transpose, sleByte),
MKLINE(Player, _detune, sleInt8),
MKLINE(Player, _vol_chan, sleUint16),
MKLINE(Player, _vol_eff, sleByte),
MKLINE(Player, _speed, sleByte),
MKLINE(Player, _song_index, sleUint16),
MKLINE(Player, _track_index, sleUint16),
MKLINE(Player, _timer_counter, sleUint16),
MKLINE(Player, _loop_to_beat, sleUint16),
MKLINE(Player, _loop_from_beat, sleUint16),
MKLINE(Player, _loop_counter, sleUint16),
MKLINE(Player, _loop_to_tick, sleUint16),
MKLINE(Player, _loop_from_tick, sleUint16),
MKLINE(Player, _tempo, sleUint32),
MKLINE(Player, _cur_pos, sleUint32),
MKLINE(Player, _next_pos, sleUint32),
MKLINE(Player, _song_offset, sleUint32),
MKLINE(Player, _tick_index, sleUint16),
MKLINE(Player, _beat_index, sleUint16),
MKLINE(Player, _ticks_per_beat, sleUint16),
MKLINE(Player, _hook._jump, sleByte),
MKLINE(Player, _hook._transpose, sleByte),
MKARRAY(Player, _hook._part_onoff[0], sleByte, 16),
MKARRAY(Player, _hook._part_volume[0], sleByte, 16),
MKARRAY(Player, _hook._part_program[0], sleByte, 16),
MKARRAY(Player, _hook._part_transpose[0], sleByte, 16),
MKEND()
};
const SaveLoadEntry volumeFaderEntries[] = {
MKREF(VolumeFader, player, TYPE_PLAYER),
MKLINE(VolumeFader, active, sleUint8),
MKLINE(VolumeFader, curvol, sleUint8),
MKLINE(VolumeFader, speed_lo_max, sleUint16),
MKLINE(VolumeFader, num_steps, sleUint16),
MKLINE(VolumeFader, speed_hi, sleInt8),
MKLINE(VolumeFader, direction, sleInt8),
MKLINE(VolumeFader, speed_lo, sleInt8),
MKLINE(VolumeFader, speed_lo_counter, sleUint16),
MKEND()
};
const SaveLoadEntry partEntries[] = {
MKREF(Part, _next, TYPE_PART),
MKREF(Part, _prev, TYPE_PART),
MKREF(Part, _player, TYPE_PLAYER),
MKLINE(Part, _pitchbend, sleInt16),
MKLINE(Part, _pitchbend_factor, sleUint8),
MKLINE(Part, _transpose, sleInt8),
MKLINE(Part, _vol, sleUint8),
MKLINE(Part, _detune, sleInt8),
MKLINE(Part, _pan, sleInt8),
MKLINE(Part, _on, sleUint8),
MKLINE(Part, _modwheel, sleUint8),
MKLINE(Part, _pedal, sleUint8),
MKLINE(Part, _program, sleUint8),
MKLINE(Part, _pri, sleUint8),
MKLINE(Part, _chan, sleUint8),
MKLINE(Part, _effect_level, sleUint8),
MKLINE(Part, _chorus, sleUint8),
MKLINE(Part, _percussion, sleUint8),
MKLINE(Part, _bank, sleUint8),
MKEND()
};
if (!ser->isSaving()) {
stop_all_sounds();
}
#ifdef _WIN32_WCE // Don't break savegames made with andys' build
if (!ser->isSaving() && ser->checkEOFLoadStream())
return 0;
#endif
ser->_ref_me = this;
ser->_saveload_ref = ser->isSaving()? ((void *)&saveReference) : ((void *)&loadReference);
ser->saveLoadEntries(this, mainEntries);
ser->saveLoadArrayOf(_players, ARRAYSIZE(_players), sizeof(_players[0]), playerEntries);
ser->saveLoadArrayOf(_parts, ARRAYSIZE(_parts), sizeof(_parts[0]), partEntries);
ser->saveLoadArrayOf(_volume_fader, ARRAYSIZE(_volume_fader),
sizeof(_volume_fader[0]), volumeFaderEntries);
if (!ser->isSaving()) {
/* Load all sounds that we need */
fix_players_after_load(scumm);
init_sustaining_notes();
_active_volume_faders = true;
fix_parts_after_load();
_driver->update_pris();
}
return 0;
}
#undef MKLINE
#undef MKEND
void IMuseInternal::fix_parts_after_load()
{
Part *part;
int i;
for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
if (part->_player)
part->fix_after_load();
}
}
/* Only call this routine from the main thread,
* since it uses getResourceAddress */
void IMuseInternal::fix_players_after_load(Scumm *scumm)
{
Player *player = _players;
int i;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
if (player->_active) {
player->set_tempo(player->_tempo);
scumm->getResourceAddress(rtSound, player->_id);
player->_mt32emulate = isMT32(player->_id);
player->_isGM = isGM(player->_id);
if (scumm->_use_adlib) {
// FIXME - This should make sure the right
// instruments are loaded, but it does not
// even try to move to the right position in
// the track. Using scan() gives a marginally
// better result, but not good enough.
//
// The correct fix is probably to store the
// Adlib instruments, or information on where
// to find them, in the savegame.
player->jump(player->_track_index, 0, 0);
}
}
}
}
void Part::set_detune(int8 detune)
{
_detune_eff = clamp((_detune = detune) + _player->_detune, -128, 127);
changed(IMuseDriver::pcMod);
}
void Part::set_pitchbend(int value)
{
_pitchbend = value;
changed(IMuseDriver::pcMod);
}
void Part::set_vol(uint8 vol)
{
_vol_eff = ((_vol = vol) + 1) * _player->_vol_eff >> 7;
changed(IMuseDriver::pcVolume);
}
void Part::set_pri(int8 pri)
{
_pri_eff = clamp((_pri = pri) + _player->_priority, 0, 255);
}
void Part::set_pan(int8 pan)
{
_pan_eff = clamp((_pan = pan) + _player->_pan, -64, 63);
changed(IMuseDriver::pcPan);
}
void Part::set_transpose(int8 transpose)
{
_transpose_eff = transpose_clamp((_transpose = transpose) + _player->_transpose, -12, 12);
changed(IMuseDriver::pcMod);
}
void Part::set_pedal(bool value)
{
_pedal = value;
changed(IMuseDriver::pcPedal);
}
void Part::set_modwheel(uint value)
{
_modwheel = value;
changed(IMuseDriver::pcModwheel);
}
void Part::set_chorus(uint chorus)
{
_chorus = chorus;
changed(IMuseDriver::pcChorus);
}
void Part::set_effect_level(uint level)
{
_effect_level = level;
changed(IMuseDriver::pcEffectLevel);
}
void Part::fix_after_load()
{
set_transpose(_transpose);
set_vol(_vol);
set_detune(_detune);
set_pri(_pri);
set_pan(_pan);
}
void Part::set_pitchbend_factor(uint8 value)
{
if (value > 12)
return;
set_pitchbend(0);
_pitchbend_factor = value;
changed (IMuseDriver::pcPitchBendFactor);
}
void Part::set_onoff(bool on)
{
if (_on != on) {
_on = on;
if (!on)
off();
if (!_percussion)
update_pris();
}
}
void Part::set_instrument(Instrument * data)
{
_drv->part_set_instrument(this, data);
}
void Part::key_on(byte note, byte velocity)
{
_drv->part_key_on(this, note, velocity);
}
void Part::key_off(byte note)
{
_drv->part_key_off(this, note);
}
void Part::init(IMuseDriver * driver)
{
_drv = driver;
_player = NULL;
_next = NULL;
_prev = NULL;
_mc = NULL;
}
void Part::setup(Player *player)
{
_player = player;
/* Insert first into player's list */
_prev = NULL;
_next = player->_parts;
if (player->_parts)
player->_parts->_prev = this;
player->_parts = this;
_percussion = true;
_on = true;
_pri_eff = player->_priority;
_pri = 0;
_vol = 127;
_vol_eff = player->_vol_eff;
_pan = clamp(player->_pan, -64, 63);
_transpose_eff = player->_transpose;
_transpose = 0;
_detune = 0;
_detune_eff = player->_detune;
_pitchbend_factor = 2;
_pitchbend = 0;
_effect_level = 64;
_program = 255;
_chorus = 0;
_modwheel = 0;
_bank = 0;
_pedal = false;
_mc = NULL;
}
void Part::uninit()
{
if (!_player)
return;
off();
/* unlink */
if (_next)
_next->_prev = _prev;
if (_prev)
_prev->_next = _next;
else
_player->_parts = _next;
_player = NULL;
_next = NULL;
_prev = NULL;
update_pris(); // In case another player couldn't allocate all its parts
}
void Part::off()
{
_drv->part_off(this);
}
void Part::changed(uint16 what)
{
_drv->part_changed(this, what);
}
void Part::set_param(byte param, int value)
{
_drv->part_set_param(this, param, value);
}
void Part::update_pris()
{
_drv->update_pris();
}
int Part::update_actives(uint16 *active)
{
return _drv->part_update_active(this, active);
}
void Part::set_program(byte program)
{
if (_program != program || _bank != 0) {
_program = program;
_bank = 0;
changed(IMuseDriver::pcProgram);
}
}
void Part::set_instrument(uint b)
{
_bank = (byte)(b >> 8);
_program = (byte)b;
changed(IMuseDriver::pcProgram);
}
//********************************************
//***** ADLIB PART OF IMUSE STARTS HERE ******
//********************************************
static byte lookup_table[64][32];
const byte volume_table[] = {
0, 4, 7, 11,
13, 16, 18, 20,
22, 24, 26, 27,
29, 30, 31, 33,
34, 35, 36, 37,
38, 39, 40, 41,
42, 43, 44, 44,
45, 46, 47, 47,
48, 49, 49, 50,
51, 51, 52, 53,
53, 54, 54, 55,
55, 56, 56, 57,
57, 58, 58, 59,
59, 60, 60, 60,
61, 61, 62, 62,
62, 63, 63, 63
};
int lookup_volume(int a, int b)
{
if (b == 0)
return 0;
if (b == 31)
return a;
if (a < -63 || a > 63) {
return b * (a + 1) >> 5;
}
if (b < 0) {
if (a < 0) {
return lookup_table[-a][-b];
} else {
return -lookup_table[a][-b];
}
} else {
if (a < 0) {
return -lookup_table[-a][b];
} else {
return lookup_table[a][b];
}
}
}
void create_lookup_table()
{
int i, j;
int sum;
for (i = 0; i < 64; i++) {
sum = i;
for (j = 0; j < 32; j++) {
lookup_table[i][j] = sum >> 5;
sum += i;
}
}
for (i = 0; i < 64; i++)
lookup_table[i][0] = 0;
}
MidiChannelAdl *IMuseAdlib::allocate_midichan(byte pri)
{
MidiChannelAdl *ac, *best = NULL;
int i;
for (i = 0; i < 9; i++) {
if (++_midichan_index >= 9)
_midichan_index = 0;
ac = &_midi_channels[_midichan_index];
if (!ac->_part)
return ac;
if (!ac->_next) {
if (ac->_part->_pri_eff <= pri) {
pri = ac->_part->_pri_eff;
best = ac;
}
}
}
if (best)
mc_off(best);
else; //debug(1, "Denying adlib channel request");
return best;
}
void IMuseAdlib::premix_proc(void *param, int16 *buf, uint len)
{
((IMuseAdlib *) param)->generate_samples(buf, len);
}
void IMuseAdlib::init(IMuseInternal *eng, OSystem *syst)
{
int i;
MidiChannelAdl *mc;
_se = eng;
for (i = 0, mc = _midi_channels; i != ARRAYSIZE(_midi_channels); i++, mc++) {
mc->_channel = i;
mc->_s11a.s10 = &mc->_s10b;
mc->_s11b.s10 = &mc->_s10a;
}
_adlib_reg_cache = (byte *)calloc(256, 1);
_opl = OPLCreate(OPL_TYPE_YM3812, 3579545, syst->property(OSystem::PROP_GET_SAMPLE_RATE, 0));
adlib_write(1, 0x20);
adlib_write(8, 0x40);
adlib_write(0xBD, 0x00);
create_lookup_table();
_mixer->setupPremix(this, premix_proc);
}
void IMuseAdlib::adlib_write(byte port, byte value)
{
if (_adlib_reg_cache[port] == value)
return;
_adlib_reg_cache[port] = value;
OPLWriteReg(_opl, port, value);
}
void IMuseAdlib::adlib_key_off(int chan)
{
byte port = chan + 0xB0;
adlib_write(port, adlib_read(port) & ~0x20);
}
struct AdlibSetParams {
byte a, b, c, d;
};
static const byte channel_mappings[9] = {
0, 1, 2, 8,
9, 10, 16, 17,
18
};
static const byte channel_mappings_2[9] = {
3, 4, 5, 11,
12, 13, 19, 20,
21
};
static const AdlibSetParams adlib_setparam_table[] = {
{0x40, 0, 63, 63}, /* level */
{0xE0, 2, 0, 0}, /* unused */
{0x40, 6, 192, 0}, /* level key scaling */
{0x20, 0, 15, 0}, /* modulator frequency multiple */
{0x60, 4, 240, 15}, /* attack rate */
{0x60, 0, 15, 15}, /* decay rate */
{0x80, 4, 240, 15}, /* sustain level */
{0x80, 0, 15, 15}, /* release rate */
{0xE0, 0, 3, 0}, /* waveform select */
{0x20, 7, 128, 0}, /* amp mod */
{0x20, 6, 64, 0}, /* vib */
{0x20, 5, 32, 0}, /* eg typ */
{0x20, 4, 16, 0}, /* ksr */
{0xC0, 0, 1, 0}, /* decay alg */
{0xC0, 1, 14, 0} /* feedback */
};
void IMuseAdlib::adlib_set_param(int channel, byte param, int value)
{
const AdlibSetParams *as;
byte port;
assert(channel >= 0 && channel < 9);
if (param <= 12) {
port = channel_mappings_2[channel];
} else if (param <= 25) {
param -= 13;
port = channel_mappings[channel];
} else if (param <= 27) {
param -= 13;
port = channel;
} else if (param == 28 || param == 29) {
if (param == 28)
value -= 15;
else
value -= 383;
value <<= 4;
channel_table_2[channel] = value;
adlib_playnote(channel, curnote_table[channel] + value);
return;
} else {
return;
}
as = &adlib_setparam_table[param];
if (as->d)
value = as->d - value;
port += as->a;
adlib_write(port, (adlib_read(port) & ~as->c) | (((byte)value) << as->b));
}
static const byte octave_numbers[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 5, 5, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7
};
static const byte note_numbers[] = {
3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14,
3, 4, 5, 6, 7, 8, 9, 10
};
static const byte note_to_f_num[] = {
90, 91, 92, 92, 93, 94, 94, 95,
96, 96, 97, 98, 98, 99, 100, 101,
101, 102, 103, 104, 104, 105, 106, 107,
107, 108, 109, 110, 111, 111, 112, 113,
114, 115, 115, 116, 117, 118, 119, 120,
121, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 132, 133, 134,
135, 136, 137, 138, 139, 140, 141, 142,
143, 145, 146, 147, 148, 149, 150, 151,
152, 153, 154, 155, 157, 158, 159, 160,
161, 162, 163, 165, 166, 167, 168, 169,
171, 172, 173, 174, 176, 177, 178, 180,
181, 182, 184, 185, 186, 188, 189, 190,
192, 193, 194, 196, 197, 199, 200, 202,
203, 205, 206, 208, 209, 211, 212, 214,
215, 217, 218, 220, 222, 223, 225, 226,
228, 230, 231, 233, 235, 236, 238, 240,
242, 243, 245, 247, 249, 251, 252, 254,
};
void IMuseAdlib::adlib_playnote(int channel, int note)
{
byte old, oct, notex;
int note2;
int i;
note2 = (note >> 7) - 4;
oct = octave_numbers[note2] << 2;
notex = note_numbers[note2];
old = adlib_read(channel + 0xB0);
if (old & 0x20) {
old &= ~0x20;
if (oct > old) {
if (notex < 6) {
notex += 12;
oct -= 4;
}
} else if (oct < old) {
if (notex > 11) {
notex -= 12;
oct += 4;
}
}
}
i = (notex << 3) + ((note >> 4) & 0x7);
adlib_write(channel + 0xA0, note_to_f_num[i]);
adlib_write(channel + 0xB0, oct | 0x20);
}
void IMuseAdlib::adlib_note_on(int chan, byte note, int mod)
{
int code;
assert(chan >= 0 && chan < 9);
code = (note << 7) + mod;
curnote_table[chan] = code;
adlib_playnote(chan, channel_table_2[chan] + code);
}
void IMuseAdlib::adlib_note_on_ex(int chan, byte note, int mod)
{
int code;
assert(chan >= 0 && chan < 9);
code = (note << 7) + mod;
curnote_table[chan] = code;
channel_table_2[chan] = 0;
adlib_playnote(chan, code);
}
void IMuseAdlib::adlib_key_onoff(int channel)
{
byte val;
byte port = channel + 0xB0;
assert(channel >= 0 && channel < 9);
val = adlib_read(port);
adlib_write(port, val & ~0x20);
adlib_write(port, val | 0x20);
}
void IMuseAdlib::adlib_setup_channel(int chan, Instrument * instr, byte vol_1, byte vol_2)
{
byte port;
assert(chan >= 0 && chan < 9);
port = channel_mappings[chan];
adlib_write(port + 0x20, instr->flags_1);
if (!(g_scumm->_features & GF_SMALL_HEADER) || (instr->feedback & 1))
adlib_write(port + 0x40, (instr->oplvl_1 | 0x3F) - vol_1 );
else
adlib_write(port + 0x40, instr->oplvl_1);
adlib_write(port + 0x60, 0xff & (~instr->atdec_1));
adlib_write(port + 0x80, 0xff & (~instr->sustrel_1));
adlib_write(port + 0xE0, instr->waveform_1);
port = channel_mappings_2[chan];
adlib_write(port + 0x20, instr->flags_2);
adlib_write(port + 0x40, (instr->oplvl_2 | 0x3F) - vol_2 );
adlib_write(port + 0x60, 0xff & (~instr->atdec_2));
adlib_write(port + 0x80, 0xff & (~instr->sustrel_2));
adlib_write(port + 0xE0, instr->waveform_2);
adlib_write((byte)chan + 0xC0, instr->feedback);
}
int IMuseAdlib::adlib_read_param(int chan, byte param)
{
const AdlibSetParams *as;
byte val;
byte port;
assert(chan >= 0 && chan < 9);
if (param <= 12) {
port = channel_mappings_2[chan];
} else if (param <= 25) {
param -= 13;
port = channel_mappings[chan];
} else if (param <= 27) {
param -= 13;
port = chan;
} else if (param == 28) {
return 0xF;
} else if (param == 29) {
return 0x17F;
} else {
return 0;
}
as = &adlib_setparam_table[param];
val = adlib_read(port + as->a);
val &= as->c;
val >>= as->b;
if (as->d)
val = as->d - val;
return val;
}
void IMuseAdlib::generate_samples(int16 *data, int len)
{
int step;
if (!_opl) {
memset(data, 0, len * sizeof(int16));
return;
}
do {
step = len;
if (step > _next_tick)
step = _next_tick;
YM3812UpdateOne(_opl, data, step);
if (!(_next_tick -= step)) {
_se->on_timer();
reset_tick();
}
data += step;
} while (len -= step);
}
void IMuseAdlib::reset_tick()
{
_next_tick = 88;
}
void IMuseAdlib::on_timer()
{
MidiChannelAdl *mc;
int i;
_adlib_timer_counter += 0xD69;
while (_adlib_timer_counter >= 0x411B) {
_adlib_timer_counter -= 0x411B;
mc = _midi_channels;
for (i = 0; i != ARRAYSIZE(_midi_channels); i++, mc++) {
if (!mc->_part)
continue;
if (mc->_duration && (mc->_duration -= 0x11) <= 0) {
mc_off(mc);
return;
}
if (mc->_s10a.active) {
mc_inc_stuff(mc, &mc->_s10a, &mc->_s11a);
}
if (mc->_s10b.active) {
mc_inc_stuff(mc, &mc->_s10b, &mc->_s11b);
}
}
}
}
const byte param_table_1[16] = {
29, 28, 27, 0,
3, 4, 7, 8,
13, 16, 17, 20,
21, 30, 31, 0
};
const uint16 param_table_2[16] = {
0x2FF, 0x1F, 0x7, 0x3F,
0x0F, 0x0F, 0x0F, 0x3,
0x3F, 0x0F, 0x0F, 0x0F,
0x3, 0x3E, 0x1F, 0
};
static const uint16 num_steps_table[] = {
1, 2, 4, 5,
6, 7, 8, 9,
10, 12, 14, 16,
18, 21, 24, 30,
36, 50, 64, 82,
100, 136, 160, 192,
240, 276, 340, 460,
600, 860, 1200, 1600
};
int IMuseAdlib::random_nr(int a)
{
static byte _rand_seed = 1;
if (_rand_seed & 1) {
_rand_seed >>= 1;
_rand_seed ^= 0xB8;
} else {
_rand_seed >>= 1;
}
return _rand_seed * a >> 8;
}
void IMuseAdlib::struct10_setup(Struct10 * s10)
{
int b, c, d, e, f, g, h;
byte t;
b = s10->unk3;
f = s10->active - 1;
t = s10->table_a[f];
e = num_steps_table[lookup_table[t & 0x7F][b]];
if (t & 0x80) {
e = random_nr(e);
}
if (e == 0)
e++;
s10->num_steps = s10->speed_lo_max = e;
if (f != 2) {
c = s10->param;
g = s10->start_value;
t = s10->table_b[f];
d = lookup_volume(c, (t & 0x7F) - 31);
if (t & 0x80) {
d = random_nr(d);
}
if (d + g > c) {
h = c - g;
} else {
h = d;
if (d + g < 0)
h = -g;
}
h -= s10->cur_val;
} else {
h = 0;
}
s10->speed_hi = h / e;
if (h < 0) {
h = -h;
s10->direction = -1;
} else {
s10->direction = 1;
}
s10->speed_lo = h % e;
s10->speed_lo_counter = 0;
}
byte IMuseAdlib::struct10_ontimer(Struct10 * s10, Struct11 * s11)
{
byte result = 0;
int i;
if (s10->count && (s10->count -= 17) <= 0) {
s10->active = 0;
return 0;
}
i = s10->cur_val + s10->speed_hi;
s10->speed_lo_counter += s10->speed_lo;
if (s10->speed_lo_counter >= s10->speed_lo_max) {
s10->speed_lo_counter -= s10->speed_lo_max;
i += s10->direction;
}
if (s10->cur_val != i || s10->modwheel != s10->modwheel_last) {
s10->cur_val = i;
s10->modwheel_last = s10->modwheel;
i = lookup_volume(i, s10->modwheel_last);
if (i != s11->modify_val) {
s11->modify_val = i;
result = 1;
}
}
if (!--s10->num_steps) {
s10->active++;
if (s10->active > 4) {
if (s10->loop) {
s10->active = 1;
result |= 2;
struct10_setup(s10);
} else {
s10->active = 0;
}
} else {
struct10_setup(s10);
}
}
return result;
}
void IMuseAdlib::struct10_init(Struct10 * s10, InstrumentExtra * ie)
{
s10->active = 1;
s10->cur_val = 0;
s10->modwheel_last = 31;
s10->count = ie->a;
if (s10->count)
s10->count *= 63;
s10->table_a[0] = ie->b;
s10->table_a[1] = ie->d;
s10->table_a[2] = ie->f;
s10->table_a[3] = ie->g;
s10->table_b[0] = ie->c;
s10->table_b[1] = ie->e;
s10->table_b[2] = 0;
s10->table_b[3] = ie->h;
struct10_setup(s10);
}
void IMuseAdlib::mc_init_stuff(MidiChannelAdl *mc, Struct10 * s10,
Struct11 * s11, byte flags, InstrumentExtra * ie)
{
Part *part = mc->_part;
s11->modify_val = 0;
s11->flag0x40 = flags & 0x40;
s10->loop = flags & 0x20;
s11->flag0x10 = flags & 0x10;
s11->param = param_table_1[flags & 0xF];
s10->param = param_table_2[flags & 0xF];
s10->unk3 = 31;
if (s11->flag0x40) {
s10->modwheel = part->_modwheel >> 2;
} else {
s10->modwheel = 31;
}
switch (s11->param) {
case 0:
s10->start_value = mc->_vol_2;
break;
case 13:
s10->start_value = mc->_vol_1;
break;
case 30:
s10->start_value = 31;
s11->s10->modwheel = 0;
break;
case 31:
s10->start_value = 0;
s11->s10->unk3 = 0;
break;
default:
s10->start_value = ((IMuseAdlib *) part->_drv)->adlib_read_param(mc->_channel, s11->param);
}
struct10_init(s10, ie);
}
void IMuseAdlib::mc_inc_stuff(MidiChannelAdl *mc, Struct10 * s10, Struct11 * s11)
{
byte code;
Part *part = mc->_part;
code = struct10_ontimer(s10, s11);
if (code & 1) {
switch (s11->param) {
case 0:
mc->_vol_2 = s10->start_value + s11->modify_val;
((IMuseAdlib *) part->_drv)->adlib_set_param(mc->_channel, 0,
volume_table[lookup_table[mc->_vol_2]
[part->_vol_eff >> 2]]);
break;
case 13:
mc->_vol_1 = s10->start_value + s11->modify_val;
if (mc->_twochan) {
((IMuseAdlib *) part->_drv)->adlib_set_param(mc->_channel, 13,
volume_table[lookup_table[mc->_vol_1]
[part->_vol_eff >> 2]]);
} else {
((IMuseAdlib *) part->_drv)->adlib_set_param(mc->_channel, 13, mc->_vol_1);
}
break;
case 30:
s11->s10->modwheel = (char)s11->modify_val;
break;
case 31:
s11->s10->unk3 = (char)s11->modify_val;
break;
default:
((IMuseAdlib *) part->_drv)->adlib_set_param(mc->_channel, s11->param,
s10->start_value + s11->modify_val);
break;
}
}
if (code & 2 && s11->flag0x10)
((IMuseAdlib *) part->_drv)->adlib_key_onoff(mc->_channel);
}
static byte map_gm_to_fm [128][30] = {
// DERIVED FROM DAY OF THE TENTACLE
{ 0x2F, 0x0B, 0x08, 0x78, 0x16, 0x24, 0x22, 0x0B, 0x9A, 0x34, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x2F, 0x0B, 0x08, 0x78, 0x16, 0x24, 0x22, 0x0B, 0x9A, 0x34, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x2F, 0x0B, 0x08, 0x78, 0x16, 0x24, 0x22, 0x0B, 0x9A, 0x34, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE2, 0x00, 0x6C, 0x77, 0x7D, 0xE4, 0x40, 0x7D, 0xA7, 0x66, 0x07, 0xA1, 0x00, 0x02, 0x1E, 0x01, 0x20, 0x01, 0x01, 0x1F, 0xAC, 0x00, 0x04, 0x24, 0x02, 0x21, 0x02, 0x02, 0x21, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xC9, 0x40, 0x3A, 0x78, 0x5E, 0xC2, 0x00, 0x4D, 0x9A, 0x7C, 0x00, 0xA1, 0xA0, 0x08, 0x1F, 0x05, 0x1E, 0x02, 0x05, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x28, 0x1D, 0x19, 0x68, 0x02, 0x22, 0x35, 0x09, 0x08, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0x28, 0x1D, 0x19, 0x68, 0x02, 0x22, 0x35, 0x09, 0x08, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xC6, 0x40, 0x3B, 0x78, 0x5E, 0xC2, 0x00, 0x4D, 0x9A, 0x7C, 0x00, 0xA1, 0xA0, 0x08, 0x1F, 0x05, 0x1E, 0x02, 0x05, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xE2, 0x00, 0x6C, 0x77, 0x7D, 0xE4, 0x40, 0x7D, 0xA7, 0x66, 0x07, 0xA1, 0x00, 0x02, 0x1E, 0x01, 0x20, 0x01, 0x01, 0x1F, 0xAC, 0x00, 0x04, 0x24, 0x02, 0x21, 0x02, 0x02, 0x21, 0x00 },
{ 0xE2, 0x28, 0x38, 0xE8, 0x02, 0xE6, 0x33, 0x0B, 0xF9, 0x00, 0x08, 0xA1, 0x00, 0x02, 0x1E, 0x02, 0x20, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0xE2, 0x28, 0x38, 0xE8, 0x02, 0xE6, 0x33, 0x0B, 0xF9, 0x00, 0x08, 0xA1, 0x00, 0x02, 0x1E, 0x02, 0x20, 0x00, 0x02, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE2, 0x00, 0x6C, 0x77, 0x7D, 0xE4, 0x40, 0x7D, 0xA7, 0x66, 0x07, 0xA1, 0x00, 0x02, 0x1E, 0x01, 0x20, 0x01, 0x01, 0x1F, 0xAC, 0x00, 0x04, 0x24, 0x02, 0x21, 0x02, 0x02, 0x21, 0x00 },
{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x84, 0x40, 0x3B, 0x5A, 0x63, 0x81, 0x00, 0x3B, 0x5A, 0x7F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
{ 0x22, 0x19, 0x79, 0x67, 0x00, 0x22, 0x3F, 0x2A, 0xC6, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xE2, 0x15, 0x7B, 0xB5, 0x02, 0xE1, 0x33, 0xAF, 0xF4, 0x36, 0x08, 0xA1, 0x00, 0x04, 0x1E, 0x04, 0x1F, 0x00, 0x04, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xF4, 0x40, 0x9F, 0xFA, 0x61, 0xE2, 0x13, 0x7F, 0xFA, 0x7D, 0x02, 0x21, 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x35, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x02, 0x00, 0x67, 0xAA, 0x65, 0x02, 0x64, 0x28, 0xF9, 0x7C, 0x08, 0x81, 0x00, 0x04, 0x1D, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x02, 0x40, 0x04, 0x9A, 0x55, 0xC2, 0x4B, 0x2B, 0xCB, 0x7C, 0x06, 0x41, 0x00, 0x00, 0x20, 0x06, 0x1C, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x02, 0x0C, 0x03, 0x6A, 0x7D, 0x02, 0x00, 0x23, 0xEA, 0x7C, 0x02, 0x81, 0x00, 0x02, 0x20, 0x01, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xF4, 0x40, 0x9F, 0xFA, 0x61, 0xE2, 0x13, 0x7F, 0xFA, 0x7D, 0x02, 0x21, 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x35, 0x00 },
{ 0xF4, 0x40, 0x9F, 0xFA, 0x61, 0xE2, 0x13, 0x7F, 0xFA, 0x7D, 0x02, 0x21, 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x35, 0x00 },
{ 0xF4, 0x40, 0x9F, 0xFA, 0x61, 0xE2, 0x13, 0x7F, 0xFA, 0x7D, 0x02, 0x21, 0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x35, 0x00 },
{ 0x02, 0x00, 0x67, 0xAA, 0x65, 0x02, 0x64, 0x28, 0xF9, 0x7C, 0x08, 0x81, 0x00, 0x04, 0x1D, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE1, 0x00, 0xCE, 0xD9, 0x4E, 0xE2, 0x00, 0x8F, 0x99, 0x65, 0x0E, 0x01, 0x00, 0x01, 0x1F, 0x00, 0x1E, 0x01, 0x01, 0x20, 0x01, 0x00, 0x00, 0x1F, 0x06, 0x1E, 0x00, 0x06, 0x1F, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xE2, 0x00, 0xCE, 0xD9, 0x4C, 0xE2, 0x00, 0x8F, 0x99, 0x64, 0x0E, 0x81, 0x10, 0x00, 0x1E, 0x05, 0x1F, 0x12, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x1F, 0x06, 0x1E, 0x00, 0x06, 0x1F, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0x7C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xF2, 0x00, 0xAF, 0xFA, 0x5C, 0xF2, 0x56, 0x9F, 0xEA, 0x7C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xF2, 0x00, 0xAF, 0xFA, 0x5C, 0xF2, 0x56, 0x9F, 0xEA, 0x7C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0x7C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0x81, 0x00, 0x01, 0x1E, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x31, 0x0C, 0x2D, 0xD7, 0x40, 0x62, 0x18, 0x2E, 0xB8, 0x7C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x00, 0x01, 0x1C, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00 },
{ 0x22, 0x10, 0x7E, 0xD8, 0x35, 0x2A, 0x2E, 0x8E, 0xD8, 0x7C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x22, 0x10, 0x7E, 0xD8, 0x35, 0x2A, 0x2E, 0x8E, 0xD8, 0x7C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE4, 0x08, 0x7E, 0x99, 0x28, 0xE6, 0x16, 0x80, 0xF8, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE4, 0x23, 0x8F, 0xF9, 0x7C, 0xE2, 0x18, 0x9F, 0x88, 0x7C, 0x01, 0x01, 0x04, 0x00, 0x1E, 0x00, 0x1F, 0x06, 0x05, 0x1F, 0x03, 0x29, 0x01, 0x1F, 0x02, 0x21, 0x01, 0x02, 0x1F, 0x00 },
{ 0xE4, 0x23, 0x8F, 0xF9, 0x7C, 0xE2, 0x18, 0x9F, 0x88, 0x7C, 0x01, 0x01, 0x04, 0x00, 0x1E, 0x00, 0x1F, 0x06, 0x05, 0x1F, 0x03, 0x29, 0x01, 0x1F, 0x02, 0x21, 0x01, 0x02, 0x1F, 0x00 },
{ 0xE4, 0x23, 0x8F, 0xF9, 0x7C, 0xE2, 0x18, 0x9F, 0x88, 0x7C, 0x01, 0x01, 0x04, 0x00, 0x1E, 0x00, 0x1F, 0x06, 0x05, 0x1F, 0x03, 0x29, 0x01, 0x1F, 0x02, 0x21, 0x01, 0x02, 0x1F, 0x00 },
{ 0x2A, 0x1E, 0x98, 0xA9, 0x00, 0x62, 0x00, 0x9F, 0xB9, 0x7C, 0x00, 0x01, 0x00, 0x01, 0x1E, 0x00, 0x1F, 0x01, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x2A, 0x1E, 0x98, 0xA9, 0x00, 0x62, 0x00, 0x9F, 0xB9, 0x7C, 0x00, 0x01, 0x00, 0x01, 0x1E, 0x00, 0x1F, 0x01, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x62, 0xA8, 0x9D, 0x84, 0x44, 0x62, 0x23, 0x7F, 0xD5, 0x4C, 0x03, 0xA1, 0x00, 0x01, 0x1E, 0x02, 0x21, 0x01, 0x01, 0x20, 0xA8, 0x00, 0x01, 0x24, 0x06, 0x20, 0x04, 0x03, 0x24, 0x00 },
{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xE4, 0x08, 0x7E, 0x99, 0x28, 0xE6, 0x16, 0x80, 0xF8, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xE4, 0x28, 0x7E, 0xF8, 0x01, 0xE2, 0x23, 0x8E, 0xE8, 0x7D, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x22, 0x10, 0x7E, 0xD8, 0x35, 0x2A, 0x2E, 0x8E, 0xD8, 0x7C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE4, 0x07, 0x05, 0xAA, 0x7C, 0xE2, 0x50, 0xBE, 0xC8, 0x7D, 0x07, 0x01, 0x00, 0x03, 0x1E, 0x01, 0x1E, 0x00, 0x00, 0x1E, 0xA8, 0x00, 0x01, 0x20, 0x06, 0x23, 0x04, 0x03, 0x20, 0x00 },
{ 0x85, 0x80, 0x05, 0xEA, 0x3D, 0x84, 0x18, 0x3C, 0xAA, 0x7C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0x7C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xF2, 0x00, 0xAF, 0xFA, 0x5C, 0xF2, 0x56, 0x9F, 0xEA, 0x7C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xE2, 0x02, 0x9F, 0xB8, 0x48, 0x22, 0x89, 0x9F, 0xE8, 0x7C, 0x00, 0x81, 0x00, 0x01, 0x1E, 0x00, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xB2, 0x25, 0xAD, 0xE9, 0x00, 0x62, 0x00, 0x8F, 0xC8, 0x7C, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xE2, 0x00, 0xCE, 0xD9, 0x4C, 0xE2, 0x00, 0x8F, 0x99, 0x64, 0x0E, 0x81, 0x10, 0x00, 0x1E, 0x05, 0x1F, 0x12, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x1F, 0x06, 0x1E, 0x00, 0x06, 0x1F, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xC9, 0x40, 0x3A, 0x38, 0x5E, 0xC2, 0x00, 0x4C, 0xAA, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xC9, 0x40, 0x3A, 0x78, 0x5E, 0xC2, 0x00, 0x4D, 0x9A, 0x7C, 0x00, 0xA1, 0xA0, 0x08, 0x1F, 0x05, 0x1E, 0x02, 0x05, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xC9, 0x40, 0x3A, 0x38, 0x5E, 0xC2, 0x00, 0x4C, 0xAA, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0xC9, 0x40, 0x3A, 0x78, 0x5E, 0xC2, 0x00, 0x4D, 0x9A, 0x7C, 0x00, 0xA1, 0xA0, 0x08, 0x1F, 0x05, 0x1E, 0x02, 0x05, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Extrapolated
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xC6, 0x00, 0x2E, 0xC7, 0x59, 0xC2, 0x06, 0x0E, 0xA7, 0x7D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x02, 0x0C, 0x03, 0x6A, 0x7D, 0x02, 0x00, 0x23, 0xEA, 0x7C, 0x02, 0x81, 0x00, 0x02, 0x20, 0x01, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xCF, 0x3B, 0x2A, 0xFE, 0x7E, 0xC0, 0xC0, 0x0C, 0xEB, 0x63, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 }, // Extrapolated
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x02, 0x0C, 0x03, 0x6A, 0x7D, 0x02, 0x00, 0x23, 0xEA, 0x7C, 0x02, 0x81, 0x00, 0x02, 0x20, 0x01, 0x1F, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x0F, 0x10, 0x10, 0x09, 0x49, 0x02, 0x12, 0x07, 0x9A, 0x7C, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0xCF, 0x3B, 0x2A, 0xFE, 0x7E, 0xC0, 0xC0, 0x0C, 0xEB, 0x63, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0xCF, 0x40, 0x0A, 0x30, 0x5C, 0xCF, 0x00, 0x0D, 0x80, 0x7C, 0x00, 0xA0, 0x00, 0x0F, 0x1E, 0x0F, 0x20, 0x00, 0x0B, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // Unknown
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // Unknown
};
void IMuseAdlib::part_changed(Part *part, uint16 what)
{
MidiChannelAdl *mc;
if (what & pcProgram) {
if (part->_player ? part->_player->_isGM : false)
part_set_instrument(part, (Instrument *) &map_gm_to_fm[part->_program]);
else if (part->_program < 32)
part_set_instrument(part, &_glob_instr[part->_program]);
}
if (what & pcMod) {
for (mc = part->_mc->adl(); mc; mc = mc->_next) {
adlib_note_on(mc->_channel, mc->_note + part->_transpose_eff,
(part->_pitchbend * part->_pitchbend_factor >> 6) + part->_detune_eff);
}
}
if (what & pcVolume) {
for (mc = part->_mc->adl(); mc; mc = mc->_next) {
adlib_set_param(mc->_channel, 0, volume_table[lookup_table[mc->_vol_2]
[part->_vol_eff >> 2]]);
if (mc->_twochan) {
adlib_set_param(mc->_channel, 13, volume_table[lookup_table[mc->_vol_1]
[part->_vol_eff >> 2]]);
}
}
}
if (what & pcPedal) {
if (!part->_pedal) {
for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
if (mc->_waitforpedal)
mc_off(mc);
}
}
}
if (what & pcModwheel) {
for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
if (mc->_s10a.active && mc->_s11a.flag0x40)
mc->_s10a.modwheel = part->_modwheel >> 2;
if (mc->_s10b.active && mc->_s11b.flag0x40)
mc->_s10b.modwheel = part->_modwheel >> 2;
}
}
}
void IMuseAdlib::mc_key_on(MidiChannel * mc2, byte note, byte velocity)
{
MidiChannelAdl *mc = (MidiChannelAdl *)mc2;
Part *part = mc->_part;
Instrument *instr = &_part_instr[part->_slot];
int c;
byte vol_1, vol_2;
mc->_twochan = instr->feedback & 1;
mc->_note = note;
mc->_waitforpedal = false;
mc->_duration = instr->duration;
if (mc->_duration != 0)
mc->_duration *= 63;
vol_1 = (instr->oplvl_1 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_1 >> 2];
if (vol_1 > 0x3F)
vol_1 = 0x3F;
mc->_vol_1 = vol_1;
vol_2 = (instr->oplvl_2 & 0x3F) + lookup_table[velocity >> 1][instr->waveform_2 >> 2];
if (vol_2 > 0x3F)
vol_2 = 0x3F;
mc->_vol_2 = vol_2;
c = part->_vol_eff >> 2;
vol_2 = volume_table[lookup_table[vol_2][c]];
if (mc->_twochan)
vol_1 = volume_table[lookup_table[vol_1][c]];
adlib_setup_channel(mc->_channel, instr, vol_1, vol_2);
adlib_note_on_ex(mc->_channel, part->_transpose_eff + note, part->_detune_eff + (part->_pitchbend * part->_pitchbend_factor >> 6));
if (instr->flags_a & 0x80) {
mc_init_stuff(mc, &mc->_s10a, &mc->_s11a, instr->flags_a, &instr->extra_a);
} else {
mc->_s10a.active = 0;
}
if (instr->flags_b & 0x80) {
mc_init_stuff(mc, &mc->_s10b, &mc->_s11b, instr->flags_b, &instr->extra_b);
} else {
mc->_s10b.active = 0;
}
}
void IMuseAdlib::set_instrument(uint slot, byte *data)
{
if (slot < 32)
memcpy(&_glob_instr[slot], data, sizeof(Instrument));
}
void IMuseAdlib::link_mc(Part *part, MidiChannelAdl *mc)
{
mc->_part = part;
mc->_next = (MidiChannelAdl *)part->_mc;
part->_mc = mc;
mc->_prev = NULL;
if (mc->_next)
mc->_next->_prev = mc;
}
void IMuseAdlib::part_key_on(Part *part, byte note, byte velocity)
{
MidiChannelAdl *mc;
if ((part->_player ? part->_player->_isGM : false) && part->_chan == 9)
return; // Jamieson630: GM percussion, not implemented for FM
mc = allocate_midichan(part->_pri_eff);
if (!mc)
return;
link_mc(part, mc);
mc_key_on(mc, note, velocity);
}
void IMuseAdlib::part_key_off(Part *part, byte note)
{
MidiChannelAdl *mc;
if ((part->_player ? part->_player->_isGM : false) && part->_chan == 9)
return; // Jamieson630: GM percussion, not implemented for FM
for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
if (mc->_note == note) {
if (part->_pedal)
mc->_waitforpedal = true;
else
mc_off(mc);
}
}
}
struct AdlibInstrSetParams {
byte param;
byte shl;
byte mask;
};
#define MKLINE(_a_,_b_,_c_) { (int)&((Instrument*)0)->_a_, _b_, ((1<<(_c_))-1)<<(_b_) }
static const AdlibInstrSetParams adlib_instr_params[69] = {
MKLINE(oplvl_2, 0, 6),
MKLINE(waveform_2, 2, 5),
MKLINE(oplvl_2, 6, 2),
MKLINE(flags_2, 0, 4),
MKLINE(atdec_2, 4, 4),
MKLINE(atdec_2, 0, 4),
MKLINE(sustrel_2, 4, 4),
MKLINE(sustrel_2, 0, 4),
MKLINE(waveform_2, 0, 2),
MKLINE(flags_2, 7, 1),
MKLINE(flags_2, 6, 1),
MKLINE(flags_2, 5, 1),
MKLINE(flags_2, 4, 1),
MKLINE(oplvl_1, 0, 6),
MKLINE(waveform_1, 2, 5),
MKLINE(oplvl_1, 6, 2),
MKLINE(flags_1, 0, 4),
MKLINE(atdec_1, 4, 4),
MKLINE(atdec_1, 0, 4),
MKLINE(sustrel_1, 4, 4),
MKLINE(sustrel_1, 0, 4),
MKLINE(waveform_1, 0, 2),
MKLINE(flags_1, 7, 1),
MKLINE(flags_1, 6, 1),
MKLINE(flags_1, 5, 1),
MKLINE(flags_1, 4, 1),
MKLINE(feedback, 0, 1),
MKLINE(feedback, 1, 3),
MKLINE(flags_a, 7, 1),
MKLINE(flags_a, 6, 1),
MKLINE(flags_a, 5, 1),
MKLINE(flags_a, 4, 1),
MKLINE(flags_a, 0, 4),
MKLINE(extra_a.a, 0, 8),
MKLINE(extra_a.b, 0, 7),
MKLINE(extra_a.c, 0, 7),
MKLINE(extra_a.d, 0, 7),
MKLINE(extra_a.e, 0, 7),
MKLINE(extra_a.f, 0, 7),
MKLINE(extra_a.g, 0, 7),
MKLINE(extra_a.h, 0, 7),
MKLINE(extra_a.b, 7, 1),
MKLINE(extra_a.c, 7, 1),
MKLINE(extra_a.d, 7, 1),
MKLINE(extra_a.e, 7, 1),
MKLINE(extra_a.f, 7, 1),
MKLINE(extra_a.g, 7, 1),
MKLINE(extra_a.h, 7, 1),
MKLINE(flags_b, 7, 1),
MKLINE(flags_b, 6, 1),
MKLINE(flags_b, 5, 1),
MKLINE(flags_b, 4, 1),
MKLINE(flags_b, 0, 4),
MKLINE(extra_b.a, 0, 8),
MKLINE(extra_b.b, 0, 7),
MKLINE(extra_b.c, 0, 7),
MKLINE(extra_b.d, 0, 7),
MKLINE(extra_b.e, 0, 7),
MKLINE(extra_b.f, 0, 7),
MKLINE(extra_b.g, 0, 7),
MKLINE(extra_b.h, 0, 7),
MKLINE(extra_b.b, 7, 1),
MKLINE(extra_b.c, 7, 1),
MKLINE(extra_b.d, 7, 1),
MKLINE(extra_b.e, 7, 1),
MKLINE(extra_b.f, 7, 1),
MKLINE(extra_b.g, 7, 1),
MKLINE(extra_b.h, 7, 1),
MKLINE(duration, 0, 8),
};
#undef MKLINE
void IMuseAdlib::part_set_param(Part *part, byte param, int value)
{
const AdlibInstrSetParams *sp = &adlib_instr_params[param];
byte *p = (byte *)&_part_instr[part->_slot] + sp->param;
*p = (*p & ~sp->mask) | (value << sp->shl);
if (param < 28) {
MidiChannelAdl *mc;
for (mc = (MidiChannelAdl *)part->_mc; mc; mc = mc->_next) {
adlib_set_param(mc->_channel, param, value);
}
}
}
void IMuseAdlib::part_off(Part *part)
{
MidiChannelAdl *mc = (MidiChannelAdl *)part->_mc;
part->_mc = NULL;
for (; mc; mc = mc->_next) {
mc_off(mc);
}
}
void IMuseAdlib::mc_off(MidiChannel * mc2)
{
MidiChannelAdl *mc = (MidiChannelAdl *)mc2, *tmp;
adlib_key_off(mc->_channel);
tmp = mc->_prev;
if (mc->_next)
mc->_next->_prev = tmp;
if (tmp)
tmp->_next = mc->_next;
else
mc->_part->_mc = mc->_next;
mc->_part = NULL;
}
void IMuseAdlib::part_set_instrument(Part *part, Instrument * instr)
{
Instrument *i = &_part_instr[part->_slot];
memcpy(i, instr, sizeof(Instrument));
}
int IMuseAdlib::part_update_active(Part *part, uint16 *active)
{
uint16 bits;
int count = 0;
MidiChannelAdl *mc;
bits = 1 << part->_chan;
for (mc = part->_mc->adl(); mc; mc = mc->_next) {
if (!(active[mc->_note] & bits)) {
active[mc->_note] |= bits;
count++;
}
}
return count;
}
//********************************************
//** GENERAL MIDI PART OF IMUSE STARTS HERE **
//********************************************
void IMuseGM::midiPitchBend(byte chan, int16 pitchbend)
{
uint16 tmp;
if (_midi_pitchbend_last[chan] != pitchbend) {
_midi_pitchbend_last[chan] = pitchbend;
tmp = pitchbend + 0x2000;
_md->send(((tmp >> 7) & 0x7F) << 16 | (tmp & 0x7F) << 8 | 0xE0 | chan);
}
}
void IMuseGM::midiPitchBendFactor (byte chan, byte factor) {
if (_midi_pitchbend_factor_last[chan] != factor) {
_midi_pitchbend_factor_last[chan] = factor;
_md->send(( 0 << 16) | (101 << 8) | (0xB0 | chan));
_md->send(( 0 << 16) | (100 << 8) | (0xB0 | chan));
_md->send((factor << 16) | ( 6 << 8) | (0xB0 | chan));
_md->send(( 0 << 16) | ( 38 << 8) | (0xB0 | chan));
}
}
void IMuseGM::midiVolume(byte chan, byte volume)
{
if (_midi_volume_last[chan] != volume) {
_midi_volume_last[chan] = volume;
_md->send(volume << 16 | 7 << 8 | 0xB0 | chan);
}
}
void IMuseGM::midiPedal(byte chan, bool pedal)
{
if (_midi_pedal_last[chan] != pedal) {
_midi_pedal_last[chan] = pedal;
_md->send(pedal << 16 | 64 << 8 | 0xB0 | chan);
}
}
void IMuseGM::midiModWheel(byte chan, byte modwheel)
{
if (_midi_modwheel_last[chan] != modwheel) {
_midi_modwheel_last[chan] = modwheel;
_md->send(modwheel << 16 | 1 << 8 | 0xB0 | chan);
}
}
void IMuseGM::midiEffectLevel(byte chan, byte level)
{
if (_midi_effectlevel_last[chan] != level) {
_midi_effectlevel_last[chan] = level;
_md->send(level << 16 | 91 << 8 | 0xB0 | chan);
}
}
void IMuseGM::midiChorus(byte chan, byte chorus)
{
if (_midi_chorus_last[chan] != chorus) {
_midi_chorus_last[chan] = chorus;
_md->send(chorus << 16 | 93 << 8 | 0xB0 | chan);
}
}
void IMuseGM::midiControl0(byte chan, byte value)
{
_md->send(value << 16 | 0 << 8 | 0xB0 | chan);
}
void IMuseGM::midiProgram(byte chan, byte program, bool mt32emulate)
{
if (mt32emulate) { /* Don't convert the percussion channel, it is the same in GM and MT32 */
if (chan != PERCUSSION_CHANNEL)
program = mt32_to_gmidi[program];
}
_md->send(program << 8 | 0xC0 | chan);
}
void IMuseGM::midiPan(byte chan, int8 pan)
{
if (_midi_pan_last[chan] != pan) {
_midi_pan_last[chan] = pan;
_md->send(((pan - 64) & 0x7F) << 16 | 10 << 8 | 0xB0 | chan);
}
}
void IMuseGM::midiNoteOn(byte chan, byte note, byte velocity)
{
_md->send(velocity << 16 | note << 8 | 0x90 | chan);
}
void IMuseGM::midiNoteOff(byte chan, byte note)
{
_md->send(note << 8 | 0x80 | chan);
}
void IMuseGM::midiSilence(byte chan)
{
_md->send((64 << 8) | 0xB0 | chan);
_md->send((123 << 8) | 0xB0 | chan);
}
void IMuseGM::part_key_on(Part *part, byte note, byte velocity)
{
MidiChannelGM *mc = part->_mc->gm();
if (mc) {
mc->_actives[note >> 4] |= (1 << (note & 0xF));
midiNoteOn(mc->_chan, note, velocity);
} else if (part->_percussion) {
midiVolume(PERCUSSION_CHANNEL, part->_vol_eff);
midiProgram(PERCUSSION_CHANNEL, part->_bank, part->_player->_mt32emulate);
midiNoteOn(PERCUSSION_CHANNEL, note, velocity);
}
}
void IMuseGM::part_key_off(Part *part, byte note)
{
MidiChannelGM *mc = part->_mc->gm();
if (mc) {
mc->_actives[note >> 4] &= ~(1 << (note & 0xF));
midiNoteOff(mc->_chan, note);
} else if (part->_percussion) {
midiNoteOff(PERCUSSION_CHANNEL, note);
}
}
#if !defined(__MORPHOS__)
int IMuseGM::midi_driver_thread(void *param)
{
IMuseGM *mid = (IMuseGM *) param;
int old_time, cur_time;
old_time = mid->_system->get_msecs();
for (;;) {
mid->_system->delay_msecs(10);
cur_time = mid->_system->get_msecs();
while (old_time < cur_time) {
old_time += 10;
mid->_se->on_timer();
}
}
}
#else
#include <proto/exec.h>
#include <proto/dos.h>
#include "morphos.h"
#include "morphos_sound.h"
int IMuseGM::midi_driver_thread(void *param)
{
IMuseGM *mid = (IMuseGM *) param;
int old_time, cur_time;
MsgPort *music_timer_port = NULL;
timerequest *music_timer_request = NULL;
ObtainSemaphore(&ScummMusicThreadRunning);
if (!OSystem_MorphOS::OpenATimer(&music_timer_port, (IORequest **) &music_timer_request, UNIT_MICROHZ, false)) {
warning("Could not open a timer - music will not play");
Wait(SIGBREAKF_CTRL_C);
}
else {
old_time = mid->_system->get_msecs();
for (;;) {
music_timer_request->tr_node.io_Command = TR_ADDREQUEST;
music_timer_request->tr_time.tv_secs = 0;
music_timer_request->tr_time.tv_micro = 10000;
DoIO((struct IORequest *)music_timer_request);
if (CheckSignal(SIGBREAKF_CTRL_C))
break;
cur_time = mid->_system->get_msecs();
while (old_time < cur_time) {
old_time += 10;
mid->_se->on_timer();
}
}
}
ReleaseSemaphore(&ScummMusicThreadRunning);
RemTask(NULL);
return 0;
}
#endif
void IMuseGM::init(IMuseInternal *eng, OSystem *syst)
{
int i;
MidiChannelGM *mc;
_system = syst;
/* open midi driver */
int result = _md->open(MidiDriver::MO_SIMPLE);
if (result)
error("IMuseGM::error = %s", MidiDriver::get_error_name(result));
/* Install the on_timer thread */
_se = eng;
syst->create_thread(midi_driver_thread, this);
for (i = 0, mc = _midi_channels; i != ARRAYSIZE(_midi_channels); i++, mc++)
mc->_chan = i;
}
void IMuseGM::uninit()
{
_md->close();
}
void IMuseGM::update_pris()
{
Part *part, *hipart;
int i;
byte hipri, lopri;
MidiChannelGM *mc, *lomc;
while (true) {
hipri = 0;
hipart = NULL;
for (i = 32, part = _se->parts_ptr(); i; i--, part++) {
if (part->_player && !part->_percussion && part->_on && !part->_mc && part->_pri_eff >= hipri) {
hipri = part->_pri_eff;
hipart = part;
}
}
if (!hipart)
return;
lopri = 255;
lomc = NULL;
for (i = ARRAYSIZE(_midi_channels), mc = _midi_channels;; mc++) {
if (!mc->_part) {
lomc = mc;
break;
}
if (mc->_part->_pri_eff <= lopri) {
lopri = mc->_part->_pri_eff;
lomc = mc;
}
if (!--i) {
if (lopri >= hipri)
return;
lomc->_part->off();
break;
}
}
hipart->_mc = lomc;
lomc->_part = hipart;
hipart->changed(pcAll);
}
}
int IMuseGM::part_update_active(Part *part, uint16 *active)
{
int i, j;
uint16 *act, mask, bits;
int count = 0;
bits = 1 << part->_chan;
act = part->_mc->gm()->_actives;
for (i = 8; i; i--) {
mask = *act++;
if (mask) {
for (j = 16; j; j--, mask >>= 1, active++) {
if (mask & 1 && !(*active & bits)) {
*active |= bits;
count++;
}
}
} else {
active += 16;
}
}
return count;
}
void IMuseGM::part_changed(Part *part, uint16 what)
{
MidiChannelGM *mc;
/* Mark for re-schedule if program changed when in pre-state */
if (what & pcProgram && part->_percussion) {
part->_percussion = false;
update_pris();
}
if (!(mc = part->_mc->gm()))
return;
if (part->_player == NULL) { /* No player, so dump phantom channel */
part->_mc = NULL;
mc->_part = NULL;
memset(mc->_actives, 0, sizeof(mc->_actives));
return;
}
if (what & pcPitchBendFactor)
midiPitchBendFactor (mc->_chan, part->_pitchbend_factor);
if (what & pcMod)
midiPitchBend(mc->_chan,
clamp(part->_pitchbend +
(part->_detune_eff * 64 / 12) +
(part->_transpose_eff * 8192 / 12), -8192, 8191));
if (what & pcVolume)
midiVolume(mc->_chan, part->_vol_eff);
if (what & pcPedal)
midiPedal(mc->_chan, part->_pedal);
if (what & pcModwheel)
midiModWheel(mc->_chan, part->_modwheel);
if (what & pcPan)
midiPan(mc->_chan, part->_pan_eff);
if (what & pcEffectLevel)
midiEffectLevel(mc->_chan, part->_effect_level);
if (what & pcProgram) {
if (part->_bank) {
midiControl0(mc->_chan, part->_bank);
midiProgram(mc->_chan, part->_program, part->_player->_mt32emulate);
midiControl0(mc->_chan, 0);
} else {
midiProgram(mc->_chan, part->_program, part->_player->_mt32emulate);
}
}
if (what & pcChorus)
midiChorus(mc->_chan, part->_effect_level);
}
void IMuseGM::part_off(Part *part)
{
MidiChannelGM *mc = part->_mc->gm();
if (mc) {
part->_mc = NULL;
mc->_part = NULL;
memset(mc->_actives, 0, sizeof(mc->_actives));
midiSilence(mc->_chan);
}
}
// The IMuse::create method provides a front-end factory
// for creating IMuseInternal without exposing that class
// to the client.
IMuse *IMuse::create(OSystem *syst, MidiDriver *midi, SoundMixer *mixer)
{
return (IMuse *) IMuseInternal::create (syst, midi, mixer);
}
/*
* IMUSE Digital Implementation, for SCUMM v7 and higher.
*/
static void imus_digital_handler(void * engine) {
g_scumm->_imuseDigital->handler();
}
IMuseDigital::IMuseDigital(Scumm *scumm) {
memset(_channel, 0, sizeof(channel) * MAX_DIGITAL_CHANNELS);
_scumm = scumm;
for (int32 l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
_channel[l]._initialized = false;
}
_scumm->_mixer->beginSlots(MAX_DIGITAL_CHANNELS + 1);
_scumm->_timer->installProcedure(imus_digital_handler, 200);
_pause = false;
}
IMuseDigital::~IMuseDigital() {
for (int32 l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
_scumm->_mixer->stop(l);
}
_scumm->_timer->releaseProcedure(imus_digital_handler);
}
struct imuse_music_table {
int16 index;
char name[30];
char title[30];
char filename[15];
int8 unk1;
};
struct imuse_music_map {
int16 room;
int16 table_index;
int16 unk1;
int16 unk2;
int16 unk3;
int16 unk4;
};
static const imuse_music_map _digStateMusicMap[] = {
{0, 0, 0, 0, 0, 0 },
{1, 0, 0, 0, 0, 0 },
{2, 2, 0, 0, 0, 0 },
{3, 47, 0, 0, 0, 0 },
{4, 3, 0, 0, 0, 0 },
{5, 3, 0, 0, 0, 0 },
{6, 3, 0, 0, 0, 0 },
{7, 3, 0, 0, 0, 0 },
{8, 4, 0, 0, 0, 0 },
{9, 5, 0, 0, 0, 0 },
{10, 4, 0, 0, 0, 0 },
{11, 44, 0, 0, 0, 0 },
{12, 5, 0, 0, 0, 0 },
{13, 1, 0, 0, 0, 0 },
{14, 5, 0, 0, 0, 0 },
{15, 6, 29, 7, 0, 0 },
{16, 8, 0, 0, 0, 0 },
{17, 1, 0, 0, 0, 0 },
{18, 9, 0, 0, 0, 0 },
{19, 9, 0, 0, 0, 0 },
{20, 6, 0, 0, 0, 0 },
{21, 6, 0, 0, 0, 0 },
{22, 44, 0, 0, 0, 0 },
{23, 10, 7, 0, 0, 0 },
{24, 26, 0, 0, 0, 0 },
{25, 17, 0, 0, 0, 0 },
{26, 17, 0, 0, 0, 0 },
{27, 18, 0, 0, 0, 0 },
{28, 1, 0, 0, 0, 0 },
{29, 20, 0, 0, 0, 0 },
{30, 22, 0, 0, 0, 0 },
{31, 23, 0, 0, 0, 0 },
{32, 22, 0, 0, 0, 0 },
{33, 26, 0, 0, 0, 0 },
{34, 24, 0, 0, 0, 0 },
{35, 1, 0, 0, 0, 0 },
{36, 1, 0, 0, 0, 0 },
{37, 42, 0, 0, 0, 0 },
{38, 43, 0, 0, 0, 0 },
{39, 44, 0, 0, 0, 0 },
{40, 1, 0, 0, 0, 0 },
{41, 43, 0, 0, 0, 0 },
{42, 44, 0, 0, 0, 0 },
{43, 43, 0, 0, 0, 0 },
{44, 45, 117,45, 114,26},
{45, 1, 0, 0, 0, 0 },
{46, 33, 6, 35, 5, 34},
{47, 1, 0, 0, 0, 0 },
{48, 43, 0, 0, 0, 0 },
{49, 44, 0, 0, 0, 0 },
{50, 1, 0, 0, 0, 0 },
{51, 1, 0, 0, 0, 0 },
{52, 0, 0, 0, 0, 0 },
{53, 28, 0, 0, 0, 0 },
{54, 28, 0, 0, 0, 0 },
{55, 29, 0, 0, 0, 0 },
{56, 29, 0, 0, 0, 0 },
{57, 29, 0, 0, 0, 0 },
{58, 31, 0, 0, 0, 0 },
{59, 1, 0, 0, 0, 0 },
{60, 37, 0, 0, 0, 0 },
{61, 39, 0, 0, 0, 0 },
{62, 38, 0, 0, 0, 0 },
{63, 39, 0, 0, 0, 0 },
{64, 39, 0, 0, 0, 0 },
{65, 40, 0, 0, 0, 0 },
{66, 1, 0, 0, 0, 0 },
{67, 40, 0, 0, 0, 0 },
{68, 39, 0, 0, 0, 0 },
{69, 1, 0, 0, 0, 0 },
{70, 49, 0, 0, 0, 0 },
{71, 1, 0, 0, 0, 0 },
{72, 1, 0, 0, 0, 0 },
{73, 50, 0, 0, 0, 0 },
{74, 1, 0, 0, 0, 0 },
{75, 51, 0, 0, 0, 0 },
{76, 1, 0, 0, 0, 0 },
{77, 52, 7, 0, 0, 0 },
{78, 63, 0, 0, 0, 0 },
{79, 1, 0, 0, 0, 0 },
{80, 41, 0, 0, 0, 0 },
{81, 48, 0, 0, 0, 0 },
{82, 21, 0, 0, 0, 0 },
{83, 27, 0, 0, 0, 0 },
{84, 1, 0, 0, 0, 0 },
{85, 1, 0, 0, 0, 0 },
{86, 0, 0, 0, 0, 0 },
{87, 1, 0, 0, 0, 0 },
{88, 32, 0, 0, 0, 0 },
{89, 33, 6, 35, 5, 34},
{90, 16, 0, 0, 0, 0 },
{91, 57, 0, 0, 0, 0 },
{92, 25, 0, 0, 0, 0 },
{93, 0, 0, 0, 0, 0 },
{94, 36, 0, 0, 0, 0 },
{95, 19, 0, 0, 0, 0 },
{96, 13, 0, 0, 0, 0 },
{97, 14, 0, 0, 0, 0 },
{98, 11, 0, 0, 0, 0 },
{99, 15, 0, 0, 0, 0 },
{100, 17, 0, 0, 0, 0 },
{101, 38, 0, 0, 0, 0 },
{102, 1, 0, 0, 0, 0 },
{103, 0, 0, 0, 0, 0 },
{104, 0, 0, 0, 0, 0 },
{105, 30, 128,29, 0, 0 },
{106, 0, 0, 0, 0, 0 },
{107, 1, 0, 0, 0, 0 },
{108, 1, 0, 0, 0, 0 },
{109, 1, 0, 0, 0, 0 },
{110, 2, 0, 0, 0, 0 },
{111, 1, 0, 0, 0, 0 },
{-1, 1, 0, 0, 0, 0 },
};
static const imuse_music_table _digStateMusicTable[] = {
{0, "STATE_NULL", "", "", 0},
{1, "stateNoChange", "", "", 0},
{2, "stateAstShip", "Asteroid (amb-ship)", "ASTERO~1.IMU", 3},
{3, "stateAstClose", "Asteroid (amb-close)", "ASTERO~2.IMU", 3},
{4, "stateAstInside", "Asteroid (inside)", "ASTERO~3.IMU", 3},
{5, "stateAstCore", "Asteroid (core)", "ASTERO~4.IMU", 3},
{6, "stateCanyonClose", "Canyon (close)", "CANYON~1.IMU", 3},
{7, "stateCanyonClose_m", "Canyon (close-m)", "CANYON~2.IMU", 3},
{8, "stateCanyonOver", "Canyon (over)", "CANYON~3.IMU", 3},
{9, "stateCanyonWreck", "Canyon (wreck)", "CANYON~4.IMU", 3},
{10, "stateNexusCanyon", "Nexus (plan)", "NEXUS(~1.IMU", 3},
{11, "stateNexusPlan", "Nexus (plan)", "NEXUS(~1.IMU", 3},
{12, "stateNexusRamp", "Nexus (ramp)", "NEXUS(~2.IMU", 3},
{13, "stateNexusMuseum", "Nexus (museum)", "NEXUS(~3.IMU", 3},
{14, "stateNexusMap", "Nexus (map)", "NEXUS(~4.IMU", 3},
{15, "stateNexusTomb", "Nexus (tomb)", "NE3706~5.IMU", 3},
{16, "stateNexusCath", "Nexus (cath)", "NE3305~5.IMU", 3},
{17, "stateNexusAirlock", "Nexus (airlock)", "NE2D3A~5.IMU", 3},
{18, "stateNexusPowerOff", "Nexus (power)", "NE8522~5.IMU", 3},
{19, "stateMuseumTramNear", "Tram (mu-near)", "TRAM(M~1.IMU", 3},
{20, "stateMuseumTramFar", "Tram (mu-far)", "TRAM(M~2.IMU", 3},
{21, "stateMuseumLockup", "Museum (lockup)", "MUSEUM~1.IMU", 3},
{22, "stateMuseumPool", "Museum (amb-pool)", "MUSEUM~2.IMU", 3},
{23, "stateMuseumSpire", "Museum (amb-spire)", "MUSEUM~3.IMU", 3},
{24, "stateMuseumMuseum", "Museum (amb-mu)", "MUSEUM~4.IMU", 3},
{25, "stateMuseumLibrary", "Museum (library)", "MUB575~5.IMU", 3},
{26, "stateMuseumCavern", "Museum (cavern)", "MUF9BE~5.IMU", 3},
{27, "stateTombTramNear", "Tram (tomb-near)", "TRAM(T~1.IMU", 3},
{28, "stateTombBase", "Tomb (amb-base)", "TOMB(A~1.IMU", 3},
{29, "stateTombSpire", "Tomb (amb-spire)", "TOMB(A~2.IMU", 3},
{30, "stateTombCave", "Tomb (amb-cave)", "TOMB(A~3.IMU", 3},
{31, "stateTombCrypt", "Tomb (crypt)", "TOMB(C~1.IMU", 3},
{32, "stateTombGuards", "Tomb (crypt-guards)", "TOMB(C~2.IMU", 3},
{33, "stateTombInner", "Tomb (inner)", "TOMB(I~1.IMU", 3},
{34, "stateTombCreator1", "Tomb (creator 1)", "TOMB(C~3.IMU", 3},
{35, "stateTombCreator2", "Tomb (creator 2)", "TOMB(C~4.IMU", 3},
{36, "statePlanTramNear", "Tram (plan-near)", "TRAM(P~1.IMU", 3},
{37, "statePlanTramFar", "Tram (plan-far)", "TRAM(P~2.IMU", 3},
{38, "statePlanBase", "Plan (amb-base)", "PLAN(A~1.IMU", 3},
{39, "statePlanSpire", "Plan (amb-spire)", "PLAN(A~2.IMU", 3},
{40, "statePlanDome", "Plan (dome)", "PLAN(D~1.IMU", 3},
{41, "stateMapTramNear", "Tram (map-near)", "TRAM(M~3.IMU", 3},
{42, "stateMapTramFar", "Tram (map-far)", "TRAM(M~4.IMU", 3},
{43, "stateMapCanyon", "Map (amb-canyon)", "MAP(AM~1.IMU", 3},
{44, "stateMapExposed", "Map (amb-exposed)", "MAP(AM~2.IMU", 3},
{45, "stateMapNestEmpty", "Map (amb-nest)", "MAP(AM~4.IMU", 3},
{46, "stateMapNestMonster", "Map (monster)", "MAP(MO~1.IMU", 3},
{47, "stateMapKlein", "Map (klein)", "MAP(KL~1.IMU", 3},
{48, "stateCathTramNear", "Tram (cath-near)", "TRAM(C~1.IMU", 3},
{49, "stateCathTramFar", "Tram (cath-far)", "TRAM(C~2.IMU", 3},
{50, "stateCathLab", "Cath (amb-inside)", "CATH(A~1.IMU", 3},
{51, "stateCathOutside", "Cath (amb-outside)", "CATH(A~2.IMU", 3},
{52, "stateWorldMuseum", "World (museum)", "WORLD(~1.IMU", 3},
{53, "stateWorldPlan", "World (plan)", "WORLD(~2.IMU", 3},
{54, "stateWorldTomb", "World (tomb)", "WORLD(~3.IMU", 3},
{55, "stateWorldMap", "World (map)", "WORLD(~4.IMU", 3},
{56, "stateWorldCath", "World (cath)", "WO3227~5.IMU", 3},
{57, "stateEye1", "Eye 1", "EYE1~1.IMU", 3},
{58, "stateEye2", "Eye 2", "EYE2~1.IMU", 3},
{59, "stateEye3", "Eye 3", "EYE3~1.IMU", 3},
{60, "stateEye4", "Eye 4", "EYE4~1.IMU", 3},
{61, "stateEye5", "Eye 5", "EYE5~1.IMU", 3},
{62, "stateEye6", "Eye 6", "EYE6~1.IMU", 3},
{63, "stateEye7", "Eye 7", "EYE7~1.IMU", 3},
{-1, "", "", "", 0},
};
static const imuse_music_table _digSeqMusicTable[] = {
{2000, "SEQ_NULL", "", "", 0},
{2005, "seqLogo", "", "", 0},
{2010, "seqIntro", "", "", 0},
{2020, "seqExplosion1b", "", "", 6},
{2030, "seqAstTunnel1a", "Seq (ast tunnel 1a)", "SEQ(AS~1.IMU", 3},
{2031, "seqAstTunnel2b", "", "", 6},
{2032, "seqAstTunnel3a", "Seq (ast tunnel 3a)", "SEQ(AS~2.IMU", 4},
{2040, "seqToPlanet1b", "", "", 5},
{2045, "seqArgBegin", "Seq (arg begin)", "SEQ(AR~1.IMU", 4},
{2046, "seqArgEnd", "Seq (arg end)", "SEQ(AR~2.IMU", 4},
{2050, "seqWreckGhost", "Seq (ghost-wreck)", "SEQ(GH~1.IMU", 4},
{2060, "seqCanyonGhost", "Seq (ghost-canyon)", "SEQ(GH~2.IMU", 4},
{2070, "seqBrinkFall", "", "", 0},
{2080, "seqPanUpCanyon", "Seq (pan up canyon)", "SEQ(PA~1.IMU", 4},
{2091, "seqAirlockTunnel1b", "", "", 6},
{2100, "seqTramToMu", "", "", 6},
{2101, "seqTramFromMu", "", "", 6},
{2102, "seqTramToTomb", "", "", 6},
{2103, "seqTramFromTomb", "", "", 6},
{2104, "seqTramToPlan", "", "", 6},
{2105, "seqTramFromPlan", "", "", 6},
{2106, "seqTramToMap", "", "", 6},
{2107, "seqTramFromMap", "", "", 6},
{2108, "seqTramToCath", "", "", 6},
{2109, "seqTramFromCath", "", "", 6},
{2110, "seqMuseumGhost", "", "", 0},
{2120, "seqSerpentAppears", "", "", 0},
{2130, "seqSerpentEats", "", "", 0},
{2140, "seqBrinkRes1b", "", "", 6},
{2141, "seqBrinkRes2a", "Seq (brink's madness)", "SEQ(BR~1.IMU", 4},
{2150, "seqLockupEntry", "Seq (brink's madness)", "SEQ(BR~1.IMU", 3},
{2160, "seqSerpentExplodes", "", "", 0},
{2170, "seqSwimUnderwater", "Seq (descent)", "SEQ(DE~1.IMU", 4},
{2175, "seqWavesPlunge", "Seq (plunge)", "SEQ(PL~1.IMU", 4},
{2180, "seqCryptOpens", "", "", 0},
{2190, "seqGuardsFight", "", "", 0},
{2200, "seqCreatorRes1.1a", "Seq (creator res 1.1a)", "SEQ(CR~1.IMU", 3},
{2201, "seqCreatorRes1.2b", "", "", 6},
{2210, "seqMaggieCapture1b", "", "", 6},
{2220, "seqStealCrystals", "Seq (brink's madness)", "SEQ(BR~1.IMU", 3},
{2230, "seqGetByMonster", "", "", 0},
{2240, "seqKillMonster1b", "", "", 6},
{2250, "seqCreatorRes2.1a", "Seq (creator res 2.1a)", "SEQ(CR~2.IMU", 3},
{2251, "seqCreatorRes2.2b", "", "", 6},
{2252, "seqCreatorRes2.3a", "Seq (creator res 2.3a)", "SEQ(CR~3.IMU", 4},
{2260, "seqMaggieInsists", "", "", 0},
{2270, "seqBrinkHelpCall", "", "", 0},
{2280, "seqBrinkCrevice1a", "Seq (brink crevice 1a)", "SEQ(BR~2.IMU", 3},
{2281, "seqBrinkCrevice2a", "Seq (brink crevice 2a)", "SEQ(BR~3.IMU", 3},
{2290, "seqCathAccess1b", "", "", 6},
{2291, "seqCathAccess2a", "Seq (cath access 2a)", "SEQ(CA~1.IMU", 4},
{2300, "seqBrinkAtGenerator", "Seq (brink's madness)", "SEQ(BR~1.IMU", 3},
{2320, "seqFightBrink1b", "", "", 6},
{2340, "seqMaggieDies1b", "", "", 6},
{2346, "seqMaggieRes1b", "", "", 6},
{2347, "seqMaggieRes2a", "Seq (maggie res 2a)", "SEQ(MA~1.IMU", 4},
{2350, "seqCreatureFalls", "", "", 0},
{2360, "seqFinale1b", "", "", 5},
{2370, "seqFinale2a", "Seq (finale 2a)", "SEQ(FI~1.IMU", 3},
{2380, "seqFinale3b1", "", "", 6},
{2390, "seqFinale3b2", "", "", 6},
{2400, "seqFinale4a", "Seq (finale 4a)", "SEQ(FI~2.IMU", 3},
{2410, "seqFinale5a", "Seq (finale 5a)", "SEQ(FI~3.IMU", 3},
{2420, "seqFinale6a", "Seq (finale 6a)", "SEQ(FI~4.IMU", 3},
{2430, "seqFinale7a", "Seq (finale 7a)", "SE3D2B~5.IMU", 3},
{2440, "seqFinale8b", "", "", 6},
{2450, "seqFinale9a", "Seq (finale 9a)", "SE313B~5.IMU", 4},
{-1, "", "", "", 0},
};
struct imuse_ft_music_table {
int16 index;
char audioname[15];
int8 unk1;
int8 volume;
char name[30];
};
static const imuse_ft_music_table _ftStateMusicTable[] = {
{0, "", 0, 0, "STATE_NULL" },
{1, "", 4, 127, "stateKstandOutside" },
{2, "kinside", 2, 127, "stateKstandInside" },
{3, "moshop", 3, 64, "stateMoesInside" },
{4, "melcut", 2, 127, "stateMoesOutside" },
{5, "mellover", 2, 127, "stateMellonAbove" },
{6, "radloop", 3, 28, "stateTrailerOutside" },
{7, "radloop", 3, 58, "stateTrailerInside" },
{8, "radloop", 3, 127, "stateTodShop" },
{9, "junkgate", 2, 127, "stateJunkGate" },
{10, "junkover", 3, 127, "stateJunkAbove" },
{11, "gastower", 2, 127, "stateGasTower" },
{12, "", 4, 0, "stateTowerAlarm" },
{13, "melcut", 2, 127, "stateCopsOnGround" },
{14, "melcut", 2, 127, "stateCopsAround" },
{15, "melcut", 2, 127, "stateMoesRuins" },
{16, "melcut", 2, 127, "stateKstandNight" },
{17, "trukblu2", 2, 127, "stateTruckerTalk" },
{18, "stretch", 2, 127, "stateMumblyPeg" },
{19, "kstand", 2, 127, "stateRanchOutside" },
{20, "kinside", 2, 127, "stateRanchInside" },
{21, "desert", 2, 127, "stateWreckedTruck" },
{22, "opening", 2, 127, "stateGorgeVista" },
{23, "caveopen", 2, 127, "stateCaveOpen" },
{24, "cavecut1", 2, 127, "stateCaveOuter" },
{25, "cavecut1", 1, 127, "stateCaveMiddle" },
{26, "cave", 2, 127, "stateCaveInner" },
{27, "corville", 2, 127, "stateCorvilleFront" },
{28, "mines", 2, 127, "stateMineField" },
{29, "bunyman3", 2, 127, "stateBunnyStore" },
{30, "stretch", 2, 127, "stateStretchBen" },
{31, "saveme", 2, 127, "stateBenPleas" },
{32, "", 4, 0, "stateBenConvinces" },
{33, "derby", 3, 127, "stateDemoDerby" },
{34, "fire", 3, 127, "stateLightMyFire" },
{35, "derby", 3, 127, "stateDerbyChase" },
{36, "carparts", 2, 127, "stateVultureCarParts"},
{37, "cavecut1", 2, 127, "stateVulturesInside" },
{38, "mines", 2, 127, "stateFactoryRear" },
{39, "croffice", 2, 127, "stateCorleyOffice" },
{40, "melcut", 2, 127, "stateCorleyHall" },
{41, "", 4, 0, "stateProjRoom" },
{42, "", 4, 0, "stateMMRoom" },
{43, "bumper", 2, 127, "stateBenOnBumper" },
{44, "benump", 2, 127, "stateBenOnBack" },
{45, "plane", 2, 127, "stateInCargoPlane" },
{46, "saveme", 2, 127, "statePlaneControls" },
{47, "", 4, 0, "stateCliffHanger1" },
{48, "", 4, 0, "stateCliffHanger2" },
{-1, "", 0, 0, "" },
};
static const imuse_ft_music_table _ftSeqMusicTable[] = {
{0, "", 2, 127, "SEQ_NULL" },
{1, "", 0, 0, "seqLogo" },
{2, "", 0, 0, "seqOpenFlick" },
{3, "", 0, 0, "seqBartender" },
{4, "opening", 2, 127, "seqBenWakes" },
{5, "", 0, 0, "seqPhotoScram" },
{6, "", 0, 0, "seqClimbChain" },
{7, "", 0, 0, "seqDogChase" },
{8, "barbeat", 2, 127, "seqDogSquish" },
{9, "barwarn", 2, 127, "seqDogHoist" },
{10, "", 0, 0, "seqCopsArrive" },
{11, "", 0, 0, "seqCopsLand" },
{12, "benwakes", 2, 127, "seqCopsLeave" },
{13, "", 0, 0, "seqCopterFlyby" },
{14, "", 0, 0, "seqCopterCrash" },
{15, "", 0, 0, "seqMoGetsParts" },
{16, "barwarn", 2, 127, "seqMoFixesBike" },
{17, "", 0, 0, "seqFirstGoodbye" },
{18, "", 0, 0, "seqCopRoadblock" },
{19, "", 0, 0, "seqDivertCops" },
{20, "swatben", 2, 127, "seqMurder" },
{21, "", 0, 0, "seqCorleyDies" },
{22, "", 0, 0, "seqTooLateAtMoes" },
{23, "", 0, 0, "seqPicture" },
{24, "dogattak", 2, 127, "seqNewsReel" },
{25, "", 0, 0, "seqCopsInspect" },
{26, "", 0, 0, "seqHijack" },
{27, "", 0, 0, "seqNestolusAtRanch" },
{28, "", 4, 0, "seqRipLimo" },
{29, "", 0, 0, "seqGorgeTurn" },
{30, "", 0, 0, "seqStealRamp" },
{31, "", 0, 0, "seqCavefishTalk" },
{32, "", 4, 0, "seqArriveCorville" },
{33, "", 0, 0, "seqSingleBunny" },
{34, "", 0, 0, "seqBunnyArmy" },
{35, "", 0, 0, "seqArriveAtMines" },
{36, "cops2", 2, 127, "seqArriveAtVultures" },
{37, "", 0, 0, "seqMakePlan" },
{38, "", 0, 0, "seqShowPlan" },
{39, "", 0, 0, "seqDerbyStart" },
{40, "cops2", 2, 127, "seqLightBales" },
{41, "", 0, 0, "seqNestolusBBQ" },
{42, "", 0, 0, "seqCallSecurity" },
{43, "", 0, 0, "seqFilmFail" },
{44, "cops2", 2, 127, "seqFilmBurn" },
{45, "", 0, 0, "seqRipSpeech" },
{46, "", 0, 0, "seqExposeRip" },
{47, "", 0, 0, "seqRipEscape" },
{48, "", 0, 0, "seqRareMoment" },
{49, "", 0, 0, "seqFanBunnies" },
{50, "", 0, 0, "seqRipDead" },
{51, "bunymrch", 2, 127, "seqFuneral" },
{52, "", 0, 0, "seqCredits" },
{-1, "", 0, 0, "" },
};
void IMuseDigital::handler() {
uint32 l = 0, i = 0;
if (_pause == true)
return;
for (l = 0; l < MAX_DIGITAL_CHANNELS;l ++) {
if (_channel[l]._used) {
if (_channel[l]._toBeRemoved == true) {
_scumm->_mixer->stop(l);
if (_scumm->_mixer->_channels[l] == NULL) {
free(_channel[l]._data);
_channel[l]._used = false;
_channel[l]._initialized = false;
}
continue;
}
if (_channel[l]._delay > 0) {
_channel[l]._delay--;
continue;
}
if (_channel[l]._volumeFade != -1) {
if (_channel[l]._volumeFadeStep < 0) {
if (_channel[l]._volume > _channel[l]._volumeFade) {
_channel[l]._volume += _channel[l]._volumeFadeStep;
_channel[l]._volumeRight += _channel[l]._volumeFadeStep;
if (_channel[l]._volume < _channel[l]._volumeFade) {
_channel[l]._volume = _channel[l]._volumeFade;
}
if (_channel[l]._volumeRight < _channel[l]._volumeFade) {
_channel[l]._volumeRight = _channel[l]._volumeFade;
}
if ((_channel[l]._volume == 0) && (_channel[l]._volumeRight == 0)) {
_channel[l]._toBeRemoved = true;
}
}
} else if (_channel[l]._volumeFadeStep > 0) {
if (_channel[l]._volume < _channel[l]._volumeFade) {
_channel[l]._volume += _channel[l]._volumeFadeStep;
_channel[l]._volumeRight += _channel[l]._volumeFadeStep;
if (_channel[l]._volume > _channel[l]._volumeFade) {
_channel[l]._volume = _channel[l]._volumeFade;
}
if (_channel[l]._volumeRight > _channel[l]._volumeFade) {
_channel[l]._volumeRight = _channel[l]._volumeFade;
}
}
}
}
if ((_channel[l]._jump[0]._numLoops == 0) && (_channel[l]._isJump == true)) {
_channel[l]._isJump = false;
}
uint32 new_size = _channel[l]._mixerSize;
uint32 mixer_size = new_size;
if (_channel[l]._initialized == false) {
mixer_size *= 2;
new_size *= 2;
}
if (_channel[l]._isJump == false) {
if (_channel[l]._offset + mixer_size > _channel[l]._size) {
new_size = _channel[l]._size - _channel[l]._offset;
if (_channel[l]._numLoops == 0) {
_channel[l]._toBeRemoved = true;
mixer_size = new_size;
}
}
} else if (_channel[l]._isJump == true) {
if (_channel[l]._jump[0]._numLoops != 500) {
_channel[l]._jump[0]._numLoops--;
}
if (_channel[l]._offset + mixer_size >= _channel[l]._jump[0]._offset) {
new_size = _channel[l]._jump[0]._offset - _channel[l]._offset;
}
}
byte *buf = (byte*)malloc(mixer_size);
memcpy(buf, _channel[l]._data + _channel[l]._offset, new_size);
if ((new_size != mixer_size) && (_channel[l]._isJump == true)) {
memcpy(buf + new_size, _channel[l]._data + _channel[l]._jump[0]._dest, mixer_size - new_size);
_channel[l]._offset = _channel[l]._jump[0]._dest + (mixer_size - new_size);
} else if ((_channel[l]._numLoops > 0) && (new_size != mixer_size)) {
memcpy(buf + new_size, _channel[l]._data, mixer_size - new_size);
_channel[l]._offset = mixer_size - new_size;
} else {
_channel[l]._offset += mixer_size;
}
if (_channel[l]._bits == 12) {
for(i = 0; i < (mixer_size / 4); i++) {
byte sample1 = buf[i * 4 + 0];
byte sample2 = buf[i * 4 + 1];
byte sample3 = buf[i * 4 + 2];
byte sample4 = buf[i * 4 + 3];
uint16 sample_a = (uint16)(((int16)((sample1 << 8) | sample2) * _channel[l]._volumeRight) >> 8);
uint16 sample_b = (uint16)(((int16)((sample3 << 8) | sample4) * _channel[l]._volume) >> 8);
buf[i * 4 + 0] = (byte)(sample_a >> 8);
buf[i * 4 + 1] = (byte)(sample_a & 0xff);
buf[i * 4 + 2] = (byte)(sample_b >> 8);
buf[i * 4 + 3] = (byte)(sample_b & 0xff);
}
} else if (_channel[l]._bits == 8) {
for(i = 0; i < (mixer_size / 2); i++) {
buf[i * 2 + 0] = (byte)(((int8)(buf[i * 2 + 0] ^ 0x80) * _channel[l]._volumeRight) >> 8) ^ 0x80;
buf[i * 2 + 1] = (byte)(((int8)(buf[i * 2 + 1] ^ 0x80) * _channel[l]._volume) >> 8) ^ 0x80;
}
}
if (_channel[l]._initialized == false) {
_scumm->_mixer->playStream(NULL, l, buf, mixer_size,
_channel[l]._freq, _channel[l]._mixerFlags, 3, 2000000);
_channel[l]._initialized = true;
} else {
_scumm->_mixer->append(l, buf, mixer_size, _channel[l]._freq, _channel[l]._mixerFlags);
}
}
}
}
void IMuseDigital::startSound(int sound) {
debug(2, "IMuseDigital::startSound(%d)", sound);
int32 l;
for(l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
if(_channel[l]._used == false) {
byte *ptr = _scumm->getResourceAddress(rtSound, sound);
byte *s_ptr = ptr;
if(ptr == NULL) {
warning("IMuseDigital::startSound(%d) NULL resource pointer", sound);
return;
}
_channel[l]._idSound = sound;
_channel[l]._offset = 0;
_channel[l]._numRegions = 0;
_channel[l]._numJumps = 0;
_channel[l]._volumeRight = 127;
_channel[l]._volume = 127;
_channel[l]._volumeFade = -1;
_channel[l]._volumeFadeParam = 0;
_channel[l]._delay = 1;
uint32 tag, size = 0, r, t;
if (READ_UINT32_UNALIGNED(ptr) == MKID('Crea')) {
_channel[l]._bits = 8;
_channel[l]._channels = 2;
_channel[l]._mixerSize = (22050 / 5) * 2;
_channel[l]._mixerFlags = SoundMixer::FLAG_AUTOFREE | SoundMixer::FLAG_STEREO | SoundMixer::FLAG_REVERSE_STEREO | SoundMixer::FLAG_UNSIGNED;
byte * t_ptr= _scumm->_sound->readCreativeVocFile(ptr, size, _channel[l]._freq, _channel[l]._numLoops);
if (_channel[l]._freq == 22222) {
_channel[l]._freq = 22050;
} else if (_channel[l]._freq == 10989) {
_channel[l]._freq = 11025;
}
if (_channel[l]._freq == 11025) {
_channel[l]._mixerSize /= 2;
}
size *= 2;
_channel[l]._data = (byte *)malloc(size);
for (t = 0; t < size / 2; t++) {
*(_channel[l]._data + t * 2 + 0) = *(t_ptr + t);
*(_channel[l]._data + t * 2 + 1) = *(t_ptr + t);
}
free(t_ptr);
_channel[l]._size = size;
} else if (READ_UINT32_UNALIGNED(ptr) == MKID('iMUS')) {
ptr += 16;
for (;;) {
tag = READ_BE_UINT32(ptr); ptr += 4;
switch(tag) {
case MKID_BE('FRMT'):
ptr += 12;
_channel[l]._bits = READ_BE_UINT32(ptr); ptr += 4;
_channel[l]._freq = READ_BE_UINT32(ptr); ptr += 4;
_channel[l]._channels = READ_BE_UINT32(ptr); ptr += 4;
break;
case MKID_BE('TEXT'):
size = READ_BE_UINT32(ptr); ptr += size + 4;
break;
case MKID_BE('REGN'):
ptr += 4;
if (_channel[l]._numRegions >= MAX_IMUSE_REGIONS) {
warning("IMuseDigital::startSound(%d) Not enough space for Region");
ptr += 8;
break;
}
_channel[l]._region[_channel[l]._numRegions]._offset = READ_BE_UINT32(ptr); ptr += 4;
_channel[l]._region[_channel[l]._numRegions]._length = READ_BE_UINT32(ptr); ptr += 4;
_channel[l]._numRegions++;
break;
case MKID_BE('STOP'):
ptr += 4;
_channel[l]._offsetStop = READ_BE_UINT32(ptr); ptr += 4;
break;
case MKID_BE('JUMP'):
ptr += 4;
if (_channel[l]._numJumps >= MAX_IMUSE_JUMPS) {
warning("IMuseDigital::startSound(%d) Not enough space for Jump");
ptr += 16;
break;
}
_channel[l]._jump[_channel[l]._numJumps]._offset = READ_BE_UINT32(ptr); ptr += 4;
_channel[l]._jump[_channel[l]._numJumps]._dest = READ_BE_UINT32(ptr); ptr += 4;
_channel[l]._jump[_channel[l]._numJumps]._id = READ_BE_UINT32(ptr); ptr += 4;
_channel[l]._jump[_channel[l]._numJumps]._numLoops = READ_BE_UINT32(ptr); ptr += 4;
_channel[l]._isJump = true;
_channel[l]._numJumps++;
break;
case MKID_BE('DATA'):
size = READ_BE_UINT32(ptr); ptr += 4;
break;
default:
error("IMuseDigital::startSound(%d) Unknown sfx header %c%c%c%c", tag>>24, tag>>16, tag>>8, tag);
}
if (tag == MKID_BE('DATA')) break;
}
// if ((sound == 131) || (sound == 123) || (sound == 122)) {
_channel[l]._isJump = false;
_channel[l]._numJumps = 0;
// }
uint32 header_size = ptr - s_ptr;
_channel[l]._offsetStop -= header_size;
if (_channel[l]._bits == 12) {
_channel[l]._offsetStop = (_channel[l]._offsetStop / 3) * 4;
}
for (r = 0; r < _channel[l]._numRegions; r++) {
_channel[l]._region[r]._offset -= header_size;
if (_channel[l]._bits == 12) {
_channel[l]._region[r]._offset = (_channel[l]._region[r]._offset / 3) * 4;
_channel[l]._region[r]._length = (_channel[l]._region[r]._length / 3) * 4;
}
}
if (_channel[l]._numJumps > 0) {
for (r = 0; r < _channel[l]._numJumps; r++) {
_channel[l]._jump[r]._offset -= header_size;
_channel[l]._jump[r]._dest -= header_size;
if (_channel[l]._bits == 12) {
_channel[l]._jump[r]._offset = (_channel[l]._jump[r]._offset / 3) * 4;
_channel[l]._jump[r]._dest = (_channel[l]._jump[r]._dest / 3) * 4;
}
}
}
_channel[l]._mixerSize = (22050 / 5) * 2;
_channel[l]._mixerFlags = SoundMixer::FLAG_AUTOFREE | SoundMixer::FLAG_STEREO | SoundMixer::FLAG_REVERSE_STEREO;
if (_channel[l]._bits == 12) {
_channel[l]._mixerSize *= 2;
_channel[l]._mixerFlags |= SoundMixer::FLAG_16BITS;
_channel[l]._size = _scumm->_sound->decode12BitsSample(ptr, &_channel[l]._data, size, (_channel[l]._channels == 2) ? false : true);
}
if (_channel[l]._bits == 8) {
_channel[l]._mixerFlags |= SoundMixer::FLAG_UNSIGNED;
if (_channel[l]._channels == 1) {
size *= 2;
_channel[l]._channels = 2;
_channel[l]._data = (byte *)malloc(size);
for (t = 0; t < size / 2; t++) {
*(_channel[l]._data + t * 2 + 0) = *(ptr + t);
*(_channel[l]._data + t * 2 + 1) = *(ptr + t);
}
} else {
_channel[l]._data = (byte *)malloc(size);
memcpy(_channel[l]._data, ptr, size);
}
_channel[l]._size = size;
}
if (_channel[l]._freq == 11025) {
_channel[l]._mixerSize /= 2;
}
}
_channel[l]._toBeRemoved = false;
_channel[l]._used = true;
break;
}
}
}
void IMuseDigital::stopSound(int sound) {
debug(2, "IMuseDigital::stopSound(%d)", sound);
for (int32 l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
if ((_channel[l]._idSound == sound) && (_channel[l]._used == true)) {
_channel[l]._toBeRemoved = true;
}
}
}
void IMuseDigital::stopAll() {
for (int32 l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
if (_channel[l]._used == true) {
_channel[l]._toBeRemoved = true;
}
}
}
void IMuseDigital::pause(bool pause) {
_pause = pause;
}
int32 IMuseDigital::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) {
byte cmd = a & 0xFF;
byte param = a >> 8;
int32 sample = b, r;
byte sub_cmd = c >> 8;
int8 channel = -1, l;
int8 tmp;
if (!(cmd || param))
return 1;
if (param == 0) {
switch (cmd) {
case 12:
switch (sub_cmd) {
case 5:
debug(2, "IMuseDigital::doCommand 12,5 sample(%d), param(%d)", sample, d);
return 0;
case 6: // volume control (0-127)
debug(2, "IMuseDigital::doCommand setting volume sample(%d), volume(%d)", sample, d);
for (l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
if ((_channel[l]._idSound == sample) && (_channel[l]._used == true)) {
channel = l;
break;
}
}
if (channel == -1) {
warning("IMuseDigital::doCommand 12,6 sample(%d) not exist in channels", sample);
return 1;
}
_channel[channel]._volume = d;
_channel[channel]._volumeRight = d;
if (_channel[channel]._volumeFade != -1) {
tmp = ((_channel[channel]._volumeFade - _channel[channel]._volume) * 2) / _channel[channel]._volumeFadeParam;
if ((tmp < 0) && (tmp > -2)) {
tmp = -1;
} else if ((tmp > 0) && (tmp < 2)) {
tmp = 1;
} else {
tmp /= 2;
}
_channel[channel]._volumeFadeStep = tmp;
}
return 0;
case 7: // right volume control (0-127)
debug(2, "IMuseDigital::doCommand setting right volume sample(%d),volume(%d)", sample, d);
for (l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
if ((_channel[l]._idSound == sample) && (_channel[l]._used == true)) {
channel = l;
break;
}
}
if (channel == -1) {
warning("IMuseDigital::doCommand 12,7 sample(%d) not exist in channels", sample);
return 1;
}
_channel[channel]._volumeRight = d;
return 0;
default:
warning("IMuseDigital::doCommand 12 DEFAULT sub command %d", sub_cmd);
return 1;
}
case 14:
switch (sub_cmd) {
case 6: // fade volume control
debug(2, "IMuseDigital::doCommand fading volume sample(%d),fade(%d, %d)", sample, d, e);
for (l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
if ((_channel[l]._idSound == sample) && (_channel[l]._used == true)) {
channel = l;
break;
}
}
if (channel == -1) {
warning("IMuseDigital::doCommand 14,6 sample %d not exist in channels", sample);
return 1;
}
_channel[channel]._volumeFade = d;
_channel[channel]._volumeFadeParam = e;
tmp = ((_channel[channel]._volumeFade - _channel[channel]._volume) * 2) / _channel[channel]._volumeFadeParam;
if ((tmp < 0) && (tmp > -2)) {
tmp = -1;
} else if ((tmp > 0) && (tmp < 2)) {
tmp = 1;
} else {
tmp /= 2;
}
_channel[channel]._volumeFadeStep = tmp;
return 0;
default:
warning("IMuseDigital::doCommand 14 DEFAULT sub command %d", sub_cmd);
return 1;
}
default:
warning("IMuseDigital::doCommand DEFAULT command %d", cmd);
return 1;
}
} else if (param == 16) {
switch (cmd) {
case 0: // play music (state)
debug(2, "IMuseDigital::doCommand 0x1000 (%d)", b);
if (_scumm->_gameId == GID_DIG) {
for(l = 0;; l++) {
if (_digStateMusicMap[l].room == -1) {
return 1;
}
if (_digStateMusicMap[l].room == b) {
int16 music = _digStateMusicMap[l].table_index;
debug(2, "Play imuse music: %s, %s, %s", _digStateMusicTable[music].name, _digStateMusicTable[music].title, _digStateMusicTable[music].filename);
if (_digStateMusicTable[music].filename[0] != 0) {
_scumm->_sound->playBundleMusic((char*)&_digStateMusicTable[music].filename);
}
return 0;
}
}
} else if (_scumm->_gameId == GID_FT) {
for(l = 0;; l++) {
if (_ftStateMusicTable[l].index == -1) {
return 1;
}
if (_ftStateMusicTable[l].index == b) {
debug(2, "Play imuse music: %s, %s", _ftStateMusicTable[l].name, _ftStateMusicTable[l].audioname);
if (_ftStateMusicTable[l].audioname[0] != 0) {
for(r = 0; r < _scumm->_numAudioNames; r++) {
if (strcmp(_ftStateMusicTable[l].audioname, &_scumm->_audioNames[r * 9]) == 0) {
startSound(r);
doCommand(12, r, 1536, _ftStateMusicTable[l].volume, 0, 0, 0, 0);
}
}
}
}
}
}
return 0;
case 1: // play music (seq)
debug(2, "IMuseDigital::doCommand 0x1001 (%d)", b);
if (_scumm->_gameId == GID_DIG) {
for(l = 0;; l++) {
if (_digSeqMusicTable[l].index == -1) {
return 1;
}
if ((_digSeqMusicTable[l].index == b)) {
debug(2, "Play imuse music: %s, %s, %s", _digSeqMusicTable[l].name, _digSeqMusicTable[l].title, _digSeqMusicTable[l].filename);
if (_digSeqMusicTable[l].filename[0] != 0) {
_scumm->_sound->playBundleMusic((char*)&_digSeqMusicTable[l].filename);
}
return 0;
}
}
} else if (_scumm->_gameId == GID_FT) {
for(l = 0;; l++) {
if (_ftSeqMusicTable[l].index == -1) {
return 1;
}
if (_ftSeqMusicTable[l].index == b) {
debug(2, "Play imuse music: %s, %s", _ftSeqMusicTable[l].name, _ftSeqMusicTable[l].audioname);
if (_ftSeqMusicTable[l].audioname[0] != 0) {
for(r = 0; r < _scumm->_numAudioNames; r++) {
if (strcmp(_ftSeqMusicTable[l].audioname, &_scumm->_audioNames[r * 9]) == 0) {
startSound(r);
doCommand(12, r, 1536, _ftSeqMusicTable[l].volume, 0, 0, 0, 0);
}
}
}
}
}
}
return 0;
case 2: // dummy in DIG and CMI
debug(2, "IMuseDigital::doCommand 0x1002 (%d)", b);
return 0;
case 3: // ??? (stream related)
debug(2, "IMuseDigital::doCommand 0x1003 (%d,%d)", b, c);
return 0;
default:
warning("IMuseDigital::doCommand (0x1xxx) DEFAULT command %d", cmd);
return 1;
}
}
return 1;
}
int IMuseDigital::getSoundStatus(int sound) {
debug(2, "IMuseDigital::getSoundStatus(%d)", sound);
for (int32 l = 0; l < MAX_DIGITAL_CHANNELS; l++) {
if ((_channel[l]._idSound == sound) && (_channel[l]._used == true)) {
return 1;
}
}
return 0;
}