2001-12-01 17:23:50 +00:00
|
|
|
/* ScummVM - Scumm Interpreter
|
2002-03-20 17:51:07 +00:00
|
|
|
* Copyright (C) 2001 Ludvig Strigeus
|
2003-03-06 21:46:56 +00:00
|
|
|
* Copyright (C) 2001-2003 The ScummVM project
|
2001-12-01 17:23:50 +00:00
|
|
|
*
|
|
|
|
* 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"
|
2002-08-21 16:53:11 +00:00
|
|
|
#include "scumm/scumm.h"
|
|
|
|
#include "sound/fmopl.h"
|
|
|
|
#include "sound/mididrv.h"
|
|
|
|
#include "scumm/imuse.h"
|
2002-12-18 13:22:40 +00:00
|
|
|
#include "scumm/instrument.h"
|
2002-08-21 16:53:11 +00:00
|
|
|
#include "scumm/saveload.h"
|
2002-08-29 23:45:15 +00:00
|
|
|
#include "scumm/sound.h"
|
2002-09-08 01:08:12 +00:00
|
|
|
#include "common/util.h"
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
//
|
|
|
|
// Some constants
|
|
|
|
//
|
2001-12-01 17:23:50 +00:00
|
|
|
#define TICKS_PER_BEAT 480
|
|
|
|
|
2002-11-06 21:34:38 +00:00
|
|
|
#define IMUSE_SYSEX_ID 0x7D
|
|
|
|
#define ROLAND_SYSEX_ID 0x41
|
2002-07-23 10:29:11 +00:00
|
|
|
#define PERCUSSION_CHANNEL 9
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
#define TRIGGER_ID 0
|
|
|
|
#define COMMAND_ID 1
|
|
|
|
|
2002-03-11 04:37:06 +00:00
|
|
|
#define MDPG_TAG "MDpg"
|
|
|
|
#define MDHD_TAG "MDhd"
|
2002-04-18 06:22:18 +00:00
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// 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.
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
class IMuseDriver;
|
|
|
|
struct Part;
|
|
|
|
|
|
|
|
struct HookDatas {
|
2002-07-07 18:04:03 +00:00
|
|
|
byte _jump, _transpose;
|
2002-04-14 18:13:08 +00:00
|
|
|
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 {
|
2002-04-29 11:48:33 +00:00
|
|
|
IMuseInternal *_se;
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
Part *_parts;
|
|
|
|
bool _active;
|
|
|
|
bool _scanning;
|
|
|
|
int _id;
|
|
|
|
byte _priority;
|
|
|
|
byte _volume;
|
|
|
|
int8 _pan;
|
|
|
|
int8 _transpose;
|
|
|
|
int8 _detune;
|
|
|
|
uint _vol_chan;
|
|
|
|
byte _vol_eff;
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
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;
|
2002-12-15 01:55:27 +00:00
|
|
|
uint32 _tempo_eff; // No Save
|
2002-04-14 18:13:08 +00:00
|
|
|
uint32 _cur_pos;
|
|
|
|
uint32 _next_pos;
|
|
|
|
uint32 _song_offset;
|
2002-12-15 01:55:27 +00:00
|
|
|
uint32 _timer_speed; // No Save
|
2002-04-14 18:13:08 +00:00
|
|
|
uint _tick_index;
|
|
|
|
uint _beat_index;
|
|
|
|
uint _ticks_per_beat;
|
2002-12-15 01:55:27 +00:00
|
|
|
byte _speed; // No Save
|
2002-04-14 18:13:08 +00:00
|
|
|
bool _abort;
|
|
|
|
|
|
|
|
HookDatas _hook;
|
|
|
|
|
2002-05-04 00:20:39 +00:00
|
|
|
bool _mt32emulate;
|
2002-09-29 11:11:42 +00:00
|
|
|
bool _isGM;
|
2002-05-04 00:20:39 +00:00
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Player part
|
2002-04-14 18:13:08 +00:00
|
|
|
void hook_clear();
|
|
|
|
void clear();
|
2003-03-18 16:13:52 +00:00
|
|
|
bool startSound(int sound);
|
2002-04-14 18:13:08 +00:00
|
|
|
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);
|
2002-10-10 15:06:02 +00:00
|
|
|
void maybe_jump (byte cmd, uint track, uint beat, uint tick);
|
2002-04-14 18:13:08 +00:00
|
|
|
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();
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Sequencer part
|
2002-04-14 18:13:08 +00:00
|
|
|
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);
|
2002-09-11 13:28:34 +00:00
|
|
|
bool is_fading_out();
|
2002-04-14 18:13:08 +00:00
|
|
|
void sequencer_timer();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct VolumeFader {
|
|
|
|
Player *player;
|
|
|
|
bool active;
|
|
|
|
byte curvol;
|
2002-07-07 18:04:03 +00:00
|
|
|
uint16 speed_lo_max, num_steps;
|
2002-04-14 18:13:08 +00:00
|
|
|
int8 speed_hi;
|
|
|
|
int8 direction;
|
|
|
|
int8 speed_lo;
|
|
|
|
uint16 speed_lo_counter;
|
2002-07-07 18:04:03 +00:00
|
|
|
|
|
|
|
void initialize() {
|
|
|
|
active = false;
|
2002-09-11 13:28:34 +00:00
|
|
|
}
|
|
|
|
void on_timer(bool probe);
|
|
|
|
byte fading_to();
|
2002-04-14 18:13:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct SustainingNotes {
|
|
|
|
SustainingNotes *next;
|
|
|
|
SustainingNotes *prev;
|
|
|
|
Player *player;
|
2002-07-07 18:04:03 +00:00
|
|
|
byte note, chan;
|
2002-04-14 18:13:08 +00:00
|
|
|
uint32 off_pos;
|
|
|
|
uint32 pos;
|
|
|
|
uint16 counter;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CommandQueue {
|
|
|
|
uint16 array[8];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct IsNoteCmdData {
|
|
|
|
byte chan;
|
|
|
|
byte note;
|
|
|
|
byte vel;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Part {
|
|
|
|
int _slot;
|
|
|
|
IMuseDriver *_drv;
|
|
|
|
Part *_next, *_prev;
|
|
|
|
MidiChannel *_mc;
|
|
|
|
Player *_player;
|
|
|
|
int16 _pitchbend;
|
|
|
|
byte _pitchbend_factor;
|
2002-07-07 18:04:03 +00:00
|
|
|
int8 _transpose, _transpose_eff;
|
|
|
|
byte _vol, _vol_eff;
|
|
|
|
int8 _detune, _detune_eff;
|
|
|
|
int8 _pan, _pan_eff;
|
2002-04-14 18:13:08 +00:00
|
|
|
bool _on;
|
|
|
|
byte _modwheel;
|
|
|
|
bool _pedal;
|
|
|
|
byte _program;
|
|
|
|
int8 _pri;
|
|
|
|
byte _pri_eff;
|
|
|
|
byte _chan;
|
|
|
|
byte _effect_level;
|
|
|
|
byte _chorus;
|
|
|
|
byte _percussion;
|
|
|
|
byte _bank;
|
|
|
|
|
2002-12-18 13:22:40 +00:00
|
|
|
// New abstract instrument definition
|
|
|
|
Instrument _instrument;
|
|
|
|
|
|
|
|
// Used to be in MidiDriver
|
2002-11-26 16:54:58 +00:00
|
|
|
uint16 _actives[8];
|
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
void key_on(byte note, byte velocity);
|
|
|
|
void key_off(byte note);
|
|
|
|
void set_param(byte param, int value);
|
2002-07-07 18:04:03 +00:00
|
|
|
void init(IMuseDriver * _driver);
|
2002-04-14 18:13:08 +00:00
|
|
|
void setup(Player *player);
|
|
|
|
void uninit();
|
|
|
|
void off();
|
|
|
|
void silence();
|
|
|
|
void set_instrument(uint b);
|
2003-03-06 17:58:13 +00:00
|
|
|
void set_instrument(byte *data);
|
2002-12-20 13:09:01 +00:00
|
|
|
void load_global_instrument (byte b);
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
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);
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
int update_actives(uint16 *active);
|
|
|
|
void set_pitchbend_factor(uint8 value);
|
|
|
|
void set_onoff(bool on);
|
|
|
|
void fix_after_load();
|
|
|
|
|
|
|
|
void update_pris();
|
2002-10-13 06:07:39 +00:00
|
|
|
void changed(uint16 what);
|
2002-04-14 18:13:08 +00:00
|
|
|
};
|
|
|
|
|
2002-10-14 16:42:57 +00:00
|
|
|
struct ImTrigger {
|
|
|
|
int sound;
|
|
|
|
byte id;
|
2002-11-23 16:15:33 +00:00
|
|
|
uint16 expire;
|
2002-10-14 16:42:57 +00:00
|
|
|
byte command [4];
|
|
|
|
};
|
|
|
|
|
2002-12-18 13:22:40 +00:00
|
|
|
// IMuseDriver class
|
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
class IMuseDriver {
|
|
|
|
public:
|
|
|
|
enum {
|
|
|
|
pcMod = 1,
|
|
|
|
pcVolume = 2,
|
|
|
|
pcPedal = 4,
|
|
|
|
pcModwheel = 8,
|
|
|
|
pcPan = 16,
|
|
|
|
pcEffectLevel = 32,
|
|
|
|
pcProgram = 64,
|
|
|
|
pcChorus = 128,
|
2002-10-13 06:07:39 +00:00
|
|
|
pcPitchBendFactor = 256,
|
2002-11-21 19:06:42 +00:00
|
|
|
pcPriority = 512,
|
2002-12-25 00:38:53 +00:00
|
|
|
pcAll = 1023
|
2002-04-14 18:13:08 +00:00
|
|
|
};
|
|
|
|
|
2002-12-18 13:22:40 +00:00
|
|
|
private:
|
|
|
|
IMuseInternal *_se;
|
|
|
|
OSystem *_system;
|
|
|
|
MidiDriver *_md;
|
|
|
|
Instrument _glob_instr[32]; // Adlib custom instruments
|
|
|
|
|
|
|
|
byte _midi_program_last[16];
|
|
|
|
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 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();
|
|
|
|
|
|
|
|
static void timer_callback (void *);
|
|
|
|
|
|
|
|
public:
|
|
|
|
IMuseDriver(MidiDriver *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);
|
2002-12-20 13:09:01 +00:00
|
|
|
void part_load_global_instrument (Part *part, byte slot);
|
2002-12-18 13:22:40 +00:00
|
|
|
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);
|
|
|
|
byte get_channel_program (byte channel) { return _midi_program_last [channel]; }
|
|
|
|
|
|
|
|
static int midi_driver_thread(void *param);
|
|
|
|
|
|
|
|
uint32 get_base_tempo() { return _md->getBaseTempo(); }
|
|
|
|
byte get_hardware_type() { return 5; }
|
2002-04-14 18:13:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// 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.
|
2002-12-21 21:09:36 +00:00
|
|
|
class IMuseInternal {
|
2002-07-07 18:04:03 +00:00
|
|
|
friend struct Player;
|
2002-12-21 21:09:36 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
private:
|
2002-08-21 16:07:07 +00:00
|
|
|
IMuseDriver * _driver;
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
byte **_base_sounds;
|
|
|
|
|
|
|
|
byte _locked;
|
|
|
|
byte _hardware_type;
|
|
|
|
|
|
|
|
private:
|
2002-08-21 16:07:07 +00:00
|
|
|
bool _paused;
|
2002-04-14 18:13:08 +00:00
|
|
|
bool _active_volume_faders;
|
2002-07-07 18:04:03 +00:00
|
|
|
bool _initialized;
|
2002-04-14 18:13:08 +00:00
|
|
|
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;
|
2002-12-21 21:09:36 +00:00
|
|
|
byte _master_volume; // Master volume. 0-255
|
|
|
|
byte _music_volume; // Global music volume. 0-255
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
uint16 _trigger_count;
|
2002-12-21 21:09:36 +00:00
|
|
|
ImTrigger _snm_triggers[16]; // Sam & Max triggers
|
2002-11-23 16:15:33 +00:00
|
|
|
uint16 _snm_trigger_index;
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
uint16 _channel_volume[8];
|
2002-12-15 01:55:27 +00:00
|
|
|
uint16 _channel_volume_eff[8]; // No Save
|
2002-04-14 18:13:08 +00:00
|
|
|
uint16 _volchan_table[8];
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
Player _players[8];
|
|
|
|
SustainingNotes _sustaining_notes[24];
|
|
|
|
VolumeFader _volume_fader[8];
|
|
|
|
Part _parts[32];
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
uint16 _active_notes[128];
|
|
|
|
CommandQueue _cmd_queue[64];
|
|
|
|
|
|
|
|
byte *findTag(int sound, char *tag, int index);
|
2002-05-04 00:20:39 +00:00
|
|
|
bool isMT32(int sound);
|
2002-09-29 11:11:42 +00:00
|
|
|
bool isGM(int sound);
|
2002-04-14 18:13:08 +00:00
|
|
|
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);
|
|
|
|
|
2002-11-23 16:15:33 +00:00
|
|
|
int32 ImSetTrigger (int sound, int id, int a, int b, int c, int d);
|
|
|
|
int32 ImClearTrigger (int sound, int id);
|
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
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);
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
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);
|
|
|
|
|
2002-12-08 16:14:29 +00:00
|
|
|
static int saveReference(void *me_ref, byte type, void *ref);
|
|
|
|
static void *loadReference(void *me_ref, byte type, int ref);
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
void lock();
|
|
|
|
void unlock();
|
|
|
|
|
|
|
|
public:
|
2002-10-21 07:31:51 +00:00
|
|
|
~IMuseInternal();
|
|
|
|
|
2002-07-07 18:04:03 +00:00
|
|
|
Part *parts_ptr() {
|
|
|
|
return _parts;
|
|
|
|
}
|
|
|
|
IMuseDriver *driver() {
|
|
|
|
return _driver;
|
|
|
|
}
|
2002-08-22 12:09:06 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
int initialize(OSystem *syst, MidiDriver *midi, SoundMixer *mixer);
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Public interface
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
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();
|
2002-11-06 21:34:38 +00:00
|
|
|
byte get_channel_program (byte channel) { return _driver->get_channel_program (channel); }
|
2003-03-18 16:13:52 +00:00
|
|
|
bool startSound(int sound);
|
|
|
|
int stopSound(int sound);
|
2002-04-14 18:13:08 +00:00
|
|
|
int stop_all_sounds();
|
2003-03-23 13:14:54 +00:00
|
|
|
int getSoundStatus(int sound);
|
2002-12-05 21:45:55 +00:00
|
|
|
bool get_sound_active(int sound);
|
2003-03-18 16:13:52 +00:00
|
|
|
int32 doCommand(int a, int b, int c, int d, int e, int f, int g, int h);
|
2002-04-14 18:13:08 +00:00
|
|
|
int clear_queue();
|
|
|
|
void setBase(byte **base);
|
|
|
|
|
|
|
|
uint32 property(int prop, uint32 value);
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-04-29 11:48:33 +00:00
|
|
|
static IMuseInternal *create(OSystem *syst, MidiDriver *midi, SoundMixer *mixer);
|
2002-04-14 18:13:08 +00:00
|
|
|
};
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// IMUSE helper functions
|
|
|
|
//
|
|
|
|
////////////////////////////////////////
|
2002-04-14 18:13:08 +00:00
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
static int clamp(int val, int min, int max) {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (val < min)
|
2001-12-01 17:23:50 +00:00
|
|
|
return min;
|
2002-04-11 17:19:16 +00:00
|
|
|
if (val > max)
|
2001-12-01 17:23:50 +00:00
|
|
|
return max;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
static int transpose_clamp(int a, int b, int c) {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (b > a)
|
|
|
|
a += (b - a + 11) / 12 * 12;
|
|
|
|
if (c < a)
|
|
|
|
a -= (a - c + 11) / 12 * 12;
|
2001-12-01 17:23:50 +00:00
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
static uint32 get_delta_time(byte **s) {
|
2002-04-11 17:19:16 +00:00
|
|
|
byte *d = *s, b;
|
2001-12-01 17:23:50 +00:00
|
|
|
uint32 time = 0;
|
|
|
|
do {
|
|
|
|
b = *d++;
|
2002-04-11 17:19:16 +00:00
|
|
|
time = (time << 7) | (b & 0x7F);
|
|
|
|
} while (b & 0x80);
|
2001-12-01 17:23:50 +00:00
|
|
|
*s = d;
|
|
|
|
return time;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
static uint read_word(byte *a) {
|
2002-04-11 17:19:16 +00:00
|
|
|
return (a[0] << 8) + a[1];
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
static void skip_midi_cmd(byte **song_ptr) {
|
2001-12-01 17:23:50 +00:00
|
|
|
byte *s, code;
|
|
|
|
|
|
|
|
const byte num_skip[] = {
|
2002-04-11 17:19:16 +00:00
|
|
|
2, 2, 2, 2, 1, 1, 2
|
2001-12-01 17:23:50 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
s = *song_ptr;
|
|
|
|
|
|
|
|
code = *s++;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (code < 0x80) {
|
2001-12-01 17:23:50 +00:00
|
|
|
s = NULL;
|
2002-04-11 17:19:16 +00:00
|
|
|
} else if (code < 0xF0) {
|
|
|
|
s += num_skip[(code & 0x70) >> 4];
|
2001-12-01 17:23:50 +00:00
|
|
|
} else {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) {
|
2001-12-01 17:23:50 +00:00
|
|
|
s += get_delta_time(&s);
|
|
|
|
} else {
|
|
|
|
s = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*song_ptr = s;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
static int is_note_cmd(byte **a, IsNoteCmdData * isnote) {
|
2001-12-01 17:23:50 +00:00
|
|
|
byte *s = *a;
|
|
|
|
byte code;
|
|
|
|
|
|
|
|
code = *s++;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
|
|
|
switch (code >> 4) {
|
2002-12-15 01:55:27 +00:00
|
|
|
case 8: // Key Off
|
2002-04-11 17:19:16 +00:00
|
|
|
isnote->chan = code & 0xF;
|
2001-12-01 17:23:50 +00:00
|
|
|
isnote->note = *s++;
|
|
|
|
isnote->vel = *s++;
|
|
|
|
*a = s;
|
|
|
|
return 1;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 9: // Key On
|
2002-04-11 17:19:16 +00:00
|
|
|
isnote->chan = code & 0xF;
|
2001-12-01 17:23:50 +00:00
|
|
|
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:
|
2002-04-11 17:19:16 +00:00
|
|
|
if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) {
|
2001-12-01 17:23:50 +00:00
|
|
|
s += get_delta_time(&s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*a = s;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// IMuseInternal implementation
|
|
|
|
//
|
|
|
|
////////////////////////////////////////
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-10-21 07:31:51 +00:00
|
|
|
IMuseInternal::~IMuseInternal() {
|
|
|
|
terminate();
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::lock() {
|
2001-12-01 17:23:50 +00:00
|
|
|
_locked++;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::unlock() {
|
2001-12-01 17:23:50 +00:00
|
|
|
_locked--;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
byte *IMuseInternal::findTag(int sound, char *tag, int index) {
|
2002-03-14 08:04:21 +00:00
|
|
|
byte *ptr = NULL;
|
2002-04-11 17:19:16 +00:00
|
|
|
int32 size, pos;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-12-18 13:22:40 +00:00
|
|
|
if (_base_sounds)
|
2002-04-11 17:19:16 +00:00
|
|
|
ptr = _base_sounds[sound];
|
2002-03-14 08:04:21 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (ptr == NULL) {
|
2002-08-25 15:06:53 +00:00
|
|
|
debug(1, "IMuseInternal::findTag completely failed finding sound %d", sound);
|
2002-04-24 14:13:09 +00:00
|
|
|
return NULL;
|
2002-04-25 03:37:56 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ptr += 8;
|
|
|
|
size = READ_BE_UINT32_UNALIGNED(ptr);
|
|
|
|
ptr += 4;
|
|
|
|
|
|
|
|
pos = 0;
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
while (pos < size) {
|
|
|
|
if (!memcmp(ptr + pos, tag, 4) && !index--)
|
|
|
|
return ptr + pos + 8;
|
|
|
|
pos += READ_BE_UINT32_UNALIGNED(ptr + pos + 4) + 8;
|
|
|
|
}
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-10-10 15:06:02 +00:00
|
|
|
debug(3, "IMuseInternal::findTag failed finding sound %d", sound);
|
2001-12-01 17:23:50 +00:00
|
|
|
return NULL;
|
2002-05-04 00:20:39 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
bool IMuseInternal::isMT32(int sound) {
|
2002-05-04 00:20:39 +00:00
|
|
|
byte *ptr = NULL;
|
|
|
|
uint32 tag;
|
|
|
|
|
|
|
|
if (_base_sounds)
|
|
|
|
ptr = _base_sounds[sound];
|
|
|
|
|
|
|
|
if (ptr == NULL)
|
|
|
|
return false;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-07-07 18:04:03 +00:00
|
|
|
tag = *(((uint32 *)ptr) + 1);
|
2002-05-04 00:20:39 +00:00
|
|
|
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;
|
|
|
|
}
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2002-05-04 00:20:39 +00:00
|
|
|
return false;
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
bool IMuseInternal::isGM(int sound) {
|
2002-09-29 11:11:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-03-18 16:13:52 +00:00
|
|
|
bool IMuseInternal::startSound(int sound) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Player *player;
|
|
|
|
void *mdhd;
|
|
|
|
|
2002-12-14 02:51:37 +00:00
|
|
|
// Do not start a sound if it is already set to
|
|
|
|
// start on an ImTrigger event. This fixes carnival
|
|
|
|
// music problems where a sound has been set to trigger
|
|
|
|
// at the right time, but then is started up immediately
|
|
|
|
// anyway, only to be restarted later when the trigger
|
|
|
|
// occurs.
|
|
|
|
int i;
|
|
|
|
ImTrigger *trigger = _snm_triggers;
|
|
|
|
for (i = ARRAYSIZE (_snm_triggers); i; --i, ++trigger) {
|
|
|
|
if (trigger->sound && trigger->id && trigger->command[0] == 8 && trigger->command[1] == sound)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
mdhd = findTag(sound, MDHD_TAG, 0);
|
|
|
|
if (!mdhd) {
|
2002-03-11 04:37:06 +00:00
|
|
|
mdhd = findTag(sound, MDPG_TAG, 0);
|
|
|
|
if (!mdhd) {
|
2003-03-18 16:13:52 +00:00
|
|
|
warning("SE::startSound failed: Couldn't find sound %d", sound);
|
2002-09-30 06:04:50 +00:00
|
|
|
return false;
|
2002-03-11 04:37:06 +00:00
|
|
|
}
|
2002-03-05 16:13:09 +00:00
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2002-10-21 08:17:18 +00:00
|
|
|
// If the requested sound is already playing, start it over
|
|
|
|
// from scratch. This was originally a hack to prevent Sam & Max
|
|
|
|
// iMuse messiness while upgrading the iMuse engine, but it
|
|
|
|
// is apparently necessary to deal with fade-and-restart
|
|
|
|
// race conditions that were observed in MI2. Reference
|
|
|
|
// Bug #590511 and Patch #607175 (which was reversed to fix
|
|
|
|
// an FOA regression: Bug #622606).
|
2002-10-14 16:42:57 +00:00
|
|
|
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
|
|
|
|
if (player->_active && player->_id == sound)
|
2002-10-21 08:17:18 +00:00
|
|
|
break;
|
2002-10-14 16:42:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!i)
|
|
|
|
player = allocate_player(128);
|
|
|
|
if (!player)
|
|
|
|
return false;
|
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
player->clear();
|
2003-03-23 13:14:54 +00:00
|
|
|
|
2003-03-18 16:13:52 +00:00
|
|
|
return player->startSound(sound);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
Player *IMuseInternal::allocate_player(byte priority) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Player *player = _players, *best = NULL;
|
|
|
|
int i;
|
|
|
|
byte bestpri = 255;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::init_players() {
|
2001-12-01 17:23:50 +00:00
|
|
|
Player *player = _players;
|
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
player->_active = false;
|
2002-04-11 17:19:16 +00:00
|
|
|
player->_se = this;
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::init_sustaining_notes() {
|
2001-12-01 17:23:50 +00:00
|
|
|
SustainingNotes *next = NULL, *sn = _sustaining_notes;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
_sustain_notes_used = NULL;
|
|
|
|
_sustain_notes_head = NULL;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_sustaining_notes); i != 0; i--, sn++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
sn->next = next;
|
|
|
|
next = sn;
|
|
|
|
}
|
|
|
|
_sustain_notes_free = next;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::init_volume_fader() {
|
2001-12-01 17:23:50 +00:00
|
|
|
VolumeFader *vf = _volume_fader;
|
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_volume_fader); i != 0; i--, vf++)
|
2001-12-01 17:23:50 +00:00
|
|
|
vf->initialize();
|
|
|
|
|
|
|
|
_active_volume_faders = false;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::init_parts() {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = 0, part = _parts; i != ARRAYSIZE(_parts); i++, part++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
part->init(_driver);
|
|
|
|
part->_slot = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-18 16:13:52 +00:00
|
|
|
int IMuseInternal::stopSound(int sound) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Player *player = _players;
|
|
|
|
int i;
|
|
|
|
int r = -1;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
|
|
|
|
if (player->_active && player->_id == sound) {
|
2001-12-01 17:23:50 +00:00
|
|
|
player->clear();
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::stop_all_sounds() {
|
2001-12-01 17:23:50 +00:00
|
|
|
Player *player = _players;
|
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (player->_active)
|
|
|
|
player->clear();
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::on_timer() {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (_locked || _paused)
|
|
|
|
return;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
lock();
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
sequencer_timers();
|
|
|
|
expire_sustain_notes();
|
|
|
|
expire_volume_faders();
|
|
|
|
_driver->on_timer();
|
|
|
|
|
|
|
|
unlock();
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::sequencer_timers() {
|
2001-12-01 17:23:50 +00:00
|
|
|
Player *player = _players;
|
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (player->_active)
|
|
|
|
player->sequencer_timer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::handle_marker(uint id, byte data) {
|
2001-12-01 17:23:50 +00:00
|
|
|
uint16 *p;
|
2002-04-11 17:19:16 +00:00
|
|
|
uint pos;
|
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
pos = _queue_end;
|
|
|
|
if (pos == _queue_pos)
|
|
|
|
return;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
|
|
|
if (_queue_adding && _queue_sound == id && data == _queue_marker)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
p = _cmd_queue[pos].array;
|
|
|
|
|
|
|
|
if (p[0] != TRIGGER_ID || p[1] != id || p[2] != data)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_trigger_count--;
|
|
|
|
_queue_cleared = false;
|
|
|
|
do {
|
2002-04-11 17:19:16 +00:00
|
|
|
pos = (pos + 1) & (ARRAYSIZE(_cmd_queue) - 1);
|
2001-12-01 17:23:50 +00:00
|
|
|
if (_queue_pos == pos)
|
|
|
|
break;
|
|
|
|
p = _cmd_queue[pos].array;
|
|
|
|
if (*p++ != COMMAND_ID)
|
|
|
|
break;
|
|
|
|
_queue_end = pos;
|
|
|
|
|
2003-03-18 16:13:52 +00:00
|
|
|
doCommand(p[0], p[1], p[2], p[3], p[4], p[5], p[6], 0);
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
if (_queue_cleared)
|
|
|
|
return;
|
|
|
|
pos = _queue_end;
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
_queue_end = pos;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::get_channel_volume(uint a) {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (a < 8)
|
2001-12-01 17:23:50 +00:00
|
|
|
return _channel_volume_eff[a];
|
2002-11-17 17:59:00 +00:00
|
|
|
return (_master_volume * _music_volume / 255) >> 1;
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
Part *IMuseInternal::allocate_part(byte pri) {
|
2002-04-11 17:19:16 +00:00
|
|
|
Part *part, *best = NULL;
|
2001-12-01 17:23:50 +00:00
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (!part->_player)
|
|
|
|
return part;
|
|
|
|
if (pri >= part->_pri_eff) {
|
|
|
|
pri = part->_pri_eff;
|
|
|
|
best = part;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-18 17:14:05 +00:00
|
|
|
if (best) {
|
2001-12-01 17:23:50 +00:00
|
|
|
best->uninit();
|
2002-12-18 17:14:05 +00:00
|
|
|
_driver->update_pris();
|
|
|
|
} else {
|
2001-12-01 17:23:50 +00:00
|
|
|
debug(1, "Denying part request");
|
2002-12-18 17:14:05 +00:00
|
|
|
}
|
2001-12-01 17:23:50 +00:00
|
|
|
return best;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::expire_sustain_notes() {
|
2002-04-11 17:19:16 +00:00
|
|
|
SustainingNotes *sn, *next;
|
2001-12-01 17:23:50 +00:00
|
|
|
Player *player;
|
|
|
|
uint32 counter;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (sn = _sustain_notes_head; sn; sn = next) {
|
2001-12-01 17:23:50 +00:00
|
|
|
next = sn->next;
|
|
|
|
player = sn->player;
|
|
|
|
|
|
|
|
counter = sn->counter + player->_timer_speed;
|
2002-04-11 17:19:16 +00:00
|
|
|
sn->pos += counter >> 16;
|
2002-02-24 17:25:03 +00:00
|
|
|
sn->counter = (unsigned short)counter & 0xFFFF;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
if (sn->pos >= sn->off_pos) {
|
|
|
|
player->key_off(sn->chan, sn->note);
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Unlink the node
|
2001-12-01 17:23:50 +00:00
|
|
|
if (next)
|
|
|
|
next->prev = sn->prev;
|
|
|
|
if (sn->prev)
|
|
|
|
sn->prev->next = next;
|
|
|
|
else
|
|
|
|
_sustain_notes_head = next;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// And put it in the free list
|
2001-12-01 17:23:50 +00:00
|
|
|
sn->next = _sustain_notes_free;
|
|
|
|
_sustain_notes_free = sn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::expire_volume_faders() {
|
2001-12-01 17:23:50 +00:00
|
|
|
VolumeFader *vf;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (++_volume_fader_counter & 7)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!_active_volume_faders)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_active_volume_faders = false;
|
|
|
|
vf = _volume_fader;
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_volume_fader); i != 0; i--, vf++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (vf->active) {
|
|
|
|
_active_volume_faders = true;
|
2002-09-11 13:28:34 +00:00
|
|
|
vf->on_timer(false);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void VolumeFader::on_timer(bool probe) {
|
2001-12-01 17:23:50 +00:00
|
|
|
byte newvol;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
newvol = curvol + speed_hi;
|
|
|
|
speed_lo_counter += speed_lo;
|
|
|
|
|
|
|
|
if (speed_lo_counter >= speed_lo_max) {
|
|
|
|
speed_lo_counter -= speed_lo_max;
|
|
|
|
newvol += direction;
|
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
|
|
|
|
if (curvol != newvol) {
|
2002-09-11 13:28:34 +00:00
|
|
|
curvol = newvol;
|
2001-12-01 17:23:50 +00:00
|
|
|
if (!newvol) {
|
2002-09-11 13:28:34 +00:00
|
|
|
if (!probe)
|
|
|
|
player->clear();
|
2001-12-01 17:23:50 +00:00
|
|
|
active = false;
|
|
|
|
return;
|
|
|
|
}
|
2002-09-11 13:28:34 +00:00
|
|
|
if (!probe)
|
|
|
|
player->set_vol(newvol);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
if (!--num_steps) {
|
|
|
|
active = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
byte VolumeFader::fading_to() {
|
2002-09-11 13:28:34 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-03-23 13:14:54 +00:00
|
|
|
int IMuseInternal::getSoundStatus(int sound) {
|
2001-12-01 17:23:50 +00:00
|
|
|
int i;
|
|
|
|
Player *player;
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
|
2002-09-11 13:28:34 +00:00
|
|
|
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,
|
2002-10-21 08:17:18 +00:00
|
|
|
// pretend it's not playing.
|
|
|
|
if (player->is_fading_out())
|
2002-09-11 13:28:34 +00:00
|
|
|
continue;
|
2001-12-01 17:23:50 +00:00
|
|
|
return 1;
|
2002-09-11 13:28:34 +00:00
|
|
|
}
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
return get_queue_sound_status(sound);
|
|
|
|
}
|
|
|
|
|
2003-03-23 13:14:54 +00:00
|
|
|
// This is exactly the same as getSoundStatus except that
|
2002-12-05 21:45:55 +00:00
|
|
|
// it treats sounds that are fading out just the same as
|
|
|
|
// other sounds. This is the method to use when determining
|
|
|
|
// what resources to expire from memory.
|
2003-03-06 17:58:13 +00:00
|
|
|
bool IMuseInternal::get_sound_active(int sound) {
|
2002-12-05 21:45:55 +00:00
|
|
|
int i;
|
|
|
|
Player *player;
|
|
|
|
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
|
|
|
|
if (player->_active && player->_id == (uint16)sound)
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return (get_queue_sound_status(sound) != 0);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::get_queue_sound_status(int sound) {
|
2001-12-01 17:23:50 +00:00
|
|
|
uint16 *a;
|
2002-04-11 17:19:16 +00:00
|
|
|
int i, j;
|
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
j = _queue_pos;
|
|
|
|
i = _queue_end;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
while (i != j) {
|
2001-12-01 17:23:50 +00:00
|
|
|
a = _cmd_queue[i].array;
|
|
|
|
if (a[0] == COMMAND_ID && a[1] == 8 && a[2] == (uint16)sound)
|
|
|
|
return 2;
|
2002-04-11 17:19:16 +00:00
|
|
|
i = (i + 1) & (ARRAYSIZE(_cmd_queue) - 1);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::set_volchan(int sound, int volchan) {
|
2001-12-01 17:23:50 +00:00
|
|
|
int r;
|
|
|
|
int i;
|
|
|
|
int num;
|
2002-04-11 17:19:16 +00:00
|
|
|
Player *player, *best, *sameid;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
r = get_volchan_entry(volchan);
|
2002-04-11 17:19:16 +00:00
|
|
|
if (r == -1)
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (r >= 8) {
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
|
2002-07-07 18:04:03 +00:00
|
|
|
if (player->_active && player->_id == (uint16)sound && player->_vol_chan != (uint16)volchan) {
|
2001-12-01 17:23:50 +00:00
|
|
|
player->_vol_chan = volchan;
|
|
|
|
player->set_vol(player->_volume);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
} else {
|
|
|
|
best = NULL;
|
|
|
|
num = 0;
|
|
|
|
sameid = NULL;
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (player->_active) {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (player->_vol_chan == (uint16)volchan) {
|
2001-12-01 17:23:50 +00:00
|
|
|
num++;
|
|
|
|
if (!best || player->_priority <= best->_priority)
|
|
|
|
best = player;
|
|
|
|
} else if (player->_id == (uint16)sound) {
|
|
|
|
sameid = player;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
if (sameid == NULL)
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
|
|
|
if (num >= r)
|
|
|
|
best->clear();
|
|
|
|
player->_vol_chan = volchan;
|
|
|
|
player->set_vol(player->_volume);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::clear_queue() {
|
2001-12-01 17:23:50 +00:00
|
|
|
_queue_adding = false;
|
|
|
|
_queue_cleared = true;
|
|
|
|
_queue_pos = 0;
|
|
|
|
_queue_end = 0;
|
|
|
|
_trigger_count = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::enqueue_command(int a, int b, int c, int d, int e, int f, int g) {
|
2001-12-01 17:23:50 +00:00
|
|
|
uint16 *p;
|
|
|
|
uint i;
|
|
|
|
|
|
|
|
i = _queue_pos;
|
|
|
|
|
|
|
|
if (i == _queue_end)
|
|
|
|
return -1;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (a == -1) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_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;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
i = (i + 1) & (ARRAYSIZE(_cmd_queue) - 1);
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (_queue_end != i) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_queue_pos = i;
|
|
|
|
return 0;
|
|
|
|
} else {
|
2002-04-11 17:19:16 +00:00
|
|
|
_queue_pos = (i - 1) & (ARRAYSIZE(_cmd_queue) - 1);
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::query_queue(int param) {
|
2002-04-11 17:19:16 +00:00
|
|
|
switch (param) {
|
2002-12-15 01:55:27 +00:00
|
|
|
case 0: // Get trigger count
|
2001-12-01 17:23:50 +00:00
|
|
|
return _trigger_count;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 1: // Get trigger type
|
2002-04-11 17:19:16 +00:00
|
|
|
if (_queue_end == _queue_pos)
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
|
|
|
return _cmd_queue[_queue_end].array[1];
|
2002-12-15 01:55:27 +00:00
|
|
|
case 2: // Get trigger sound
|
2002-04-11 17:19:16 +00:00
|
|
|
if (_queue_end == _queue_pos)
|
2001-12-01 17:23:50 +00:00
|
|
|
return 0xFF;
|
|
|
|
return _cmd_queue[_queue_end].array[2];
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::get_music_volume() {
|
2002-11-17 17:59:00 +00:00
|
|
|
return _music_volume;
|
2002-03-05 23:37:31 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::set_music_volume(uint vol) {
|
2002-11-17 17:59:00 +00:00
|
|
|
if (vol > 255)
|
|
|
|
vol = 255;
|
2002-07-28 15:03:45 +00:00
|
|
|
else if (vol < 0)
|
|
|
|
vol = 0;
|
2002-07-26 00:41:07 +00:00
|
|
|
|
2002-11-17 17:59:00 +00:00
|
|
|
if (_music_volume == vol)
|
|
|
|
return 0;
|
|
|
|
_music_volume = vol;
|
|
|
|
vol = vol * _master_volume / 255;
|
2002-11-18 20:05:25 +00:00
|
|
|
for (uint i = 0; i < ARRAYSIZE (_channel_volume); i++) {
|
2002-11-17 17:59:00 +00:00
|
|
|
_channel_volume_eff[i] = _channel_volume[i] * vol / 255;
|
|
|
|
}
|
|
|
|
if (!_paused)
|
|
|
|
update_volumes();
|
2002-03-05 23:37:31 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::set_master_volume (uint vol) {
|
2002-11-17 17:59:00 +00:00
|
|
|
if (vol > 255)
|
|
|
|
vol = 255;
|
|
|
|
else if (vol < 0)
|
|
|
|
vol = 0;
|
|
|
|
if (_master_volume == vol)
|
|
|
|
return 0;
|
2002-03-05 23:37:31 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
_master_volume = vol;
|
2002-11-17 17:59:00 +00:00
|
|
|
vol = vol * _music_volume / 255;
|
2002-11-18 20:05:25 +00:00
|
|
|
for (uint i = 0; i < ARRAYSIZE (_channel_volume); i++) {
|
2002-11-17 17:59:00 +00:00
|
|
|
_channel_volume_eff[i] = _channel_volume[i] * vol / 255;
|
|
|
|
}
|
2002-11-16 14:23:22 +00:00
|
|
|
if (!_paused)
|
|
|
|
update_volumes();
|
2001-12-01 17:23:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::get_master_volume() {
|
2002-11-17 17:59:00 +00:00
|
|
|
return _master_volume;
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::terminate() {
|
2002-08-22 12:09:06 +00:00
|
|
|
if (_driver) {
|
|
|
|
_driver->uninit();
|
|
|
|
delete _driver;
|
|
|
|
_driver = NULL;
|
|
|
|
}
|
2001-12-01 17:23:50 +00:00
|
|
|
return 0;
|
2002-12-15 01:55:27 +00:00
|
|
|
// Not implemented
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::enqueue_trigger(int sound, int marker) {
|
2001-12-01 17:23:50 +00:00
|
|
|
uint16 *p;
|
|
|
|
uint pos;
|
|
|
|
|
|
|
|
pos = _queue_pos;
|
|
|
|
|
|
|
|
p = _cmd_queue[pos].array;
|
|
|
|
p[0] = TRIGGER_ID;
|
|
|
|
p[1] = sound;
|
|
|
|
p[2] = marker;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
pos = (pos + 1) & (ARRAYSIZE(_cmd_queue) - 1);
|
|
|
|
if (_queue_end == pos) {
|
|
|
|
_queue_pos = (pos - 1) & (ARRAYSIZE(_cmd_queue) - 1);
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
_queue_pos = pos;
|
|
|
|
_queue_adding = true;
|
|
|
|
_queue_sound = sound;
|
|
|
|
_queue_marker = marker;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-18 16:13:52 +00:00
|
|
|
int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) {
|
2002-10-12 04:57:49 +00:00
|
|
|
int i;
|
2002-04-11 17:19:16 +00:00
|
|
|
byte cmd = a & 0xFF;
|
|
|
|
byte param = a >> 8;
|
|
|
|
Player *player = NULL;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
if (!_initialized && (cmd || param))
|
|
|
|
return -1;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (param == 0) {
|
|
|
|
switch (cmd) {
|
2001-12-01 17:23:50 +00:00
|
|
|
case 6:
|
2002-11-17 17:59:00 +00:00
|
|
|
if (b > 127)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return set_master_volume ((b << 1) | (b ? 0 : 1)); // Convert b from 0-127 to 0-255
|
2001-12-01 17:23:50 +00:00
|
|
|
case 7:
|
2002-11-17 17:59:00 +00:00
|
|
|
return _master_volume >> 1; // Convert from 0-255 to 0-127
|
2001-12-01 17:23:50 +00:00
|
|
|
case 8:
|
2003-03-18 16:13:52 +00:00
|
|
|
return startSound(b) ? 0 : -1;
|
2001-12-01 17:23:50 +00:00
|
|
|
case 9:
|
2003-03-18 16:13:52 +00:00
|
|
|
return stopSound(b);
|
2002-10-10 15:16:58 +00:00
|
|
|
case 10: // FIXME: Sam and Max - Not sure if this is correct
|
|
|
|
return stop_all_sounds();
|
2001-12-01 17:23:50 +00:00
|
|
|
case 11:
|
|
|
|
return stop_all_sounds();
|
2002-10-14 16:42:57 +00:00
|
|
|
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.
|
|
|
|
return player->set_vol (e);
|
|
|
|
default:
|
2003-03-18 16:13:52 +00:00
|
|
|
warning("IMuseInternal::doCommand (6) unsupported sub-command %d", d);
|
2002-10-14 16:42:57 +00:00
|
|
|
}
|
|
|
|
return -1;
|
2001-12-01 17:23:50 +00:00
|
|
|
case 13:
|
2003-03-23 13:14:54 +00:00
|
|
|
return getSoundStatus(b);
|
2002-10-12 04:57:49 +00:00
|
|
|
case 14:
|
|
|
|
// Sam and Max: Volume Fader?
|
2002-10-14 16:42:57 +00:00
|
|
|
// Prevent instantaneous volume fades.
|
|
|
|
// Fixes a Ball of Twine issue, but might not be the right long-term solution.
|
2002-10-13 05:39:26 +00:00
|
|
|
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;
|
|
|
|
}
|
2002-10-12 04:57:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
case 15:
|
2002-12-14 02:51:37 +00:00
|
|
|
// Sam & Max: Set hook for a "maybe" jump
|
2002-10-12 04:57:49 +00:00
|
|
|
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
|
|
|
|
if (player->_active && player->_id == (uint16)b) {
|
2002-10-12 14:43:00 +00:00
|
|
|
player->_hook._jump = d;
|
2002-10-12 04:57:49 +00:00
|
|
|
return 0;
|
2002-05-10 20:21:12 +00:00
|
|
|
}
|
2002-07-07 18:04:03 +00:00
|
|
|
}
|
2002-10-12 04:57:49 +00:00
|
|
|
return -1;
|
|
|
|
case 16:
|
|
|
|
return set_volchan(b, c);
|
|
|
|
case 17:
|
2002-10-14 16:42:57 +00:00
|
|
|
if (g_scumm->_gameId != GID_SAMNMAX) {
|
|
|
|
return set_channel_volume(b, c);
|
|
|
|
} else {
|
2002-11-23 16:15:33 +00:00
|
|
|
if (e || f || g || h)
|
|
|
|
return ImSetTrigger (b, d, e, f, g, h);
|
|
|
|
else
|
|
|
|
return ImClearTrigger (b, d);
|
2002-07-07 18:04:03 +00:00
|
|
|
}
|
2001-12-01 17:23:50 +00:00
|
|
|
case 18:
|
2002-10-14 16:42:57 +00:00
|
|
|
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;
|
|
|
|
}
|
2002-04-21 17:54:59 +00:00
|
|
|
case 19:
|
2002-10-14 16:42:57 +00:00
|
|
|
// Sam & Max: ImClearTrigger
|
|
|
|
// This should clear a trigger that's been set up
|
|
|
|
// with ImSetTrigger (cmd == 17). Seems to work....
|
2002-11-23 16:15:33 +00:00
|
|
|
return ImClearTrigger (b, d);
|
2002-12-13 15:49:30 +00:00
|
|
|
case 20:
|
|
|
|
// Sam & Max: Deferred Command
|
|
|
|
// FIXME: Right now this acts as an immediate command.
|
|
|
|
// The significance of parameter b is unknown.
|
2003-03-18 16:13:52 +00:00
|
|
|
warning ("Incomplete support for iMuse::doCommand(20)");
|
|
|
|
return doCommand (c, d, e, f, g, h, 0, 0);
|
2002-04-14 18:13:08 +00:00
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
return 0;
|
2001-12-01 17:23:50 +00:00
|
|
|
default:
|
2003-03-18 16:13:52 +00:00
|
|
|
warning("doCommand (%d [%d/%d], %d, %d, %d, %d, %d, %d, %d) unsupported", a, param, cmd, b, c, d, e, f, g, h);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
} else if (param == 1) {
|
|
|
|
if ((1 << cmd) & (0x783FFF)) {
|
2001-12-01 17:23:50 +00:00
|
|
|
player = get_player_byid(b);
|
|
|
|
if (!player)
|
|
|
|
return -1;
|
2002-04-11 17:19:16 +00:00
|
|
|
if ((1 << cmd) & (1 << 11 | 1 << 22)) {
|
|
|
|
assert(c >= 0 && c <= 15);
|
|
|
|
player = (Player *)player->get_part(c);
|
|
|
|
if (!player)
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
|
|
|
|
switch (cmd) {
|
2001-12-01 17:23:50 +00:00
|
|
|
case 0:
|
2002-11-10 18:34:57 +00:00
|
|
|
if (g_scumm->_gameId == GID_SAMNMAX) {
|
2002-12-13 15:16:40 +00:00
|
|
|
if (d == 1) // Measure number
|
|
|
|
return ((player->_beat_index - 1) >> 2) + 1;
|
|
|
|
else if (d == 2) // Beat number
|
2002-11-10 18:34:57 +00:00
|
|
|
return player->_beat_index;
|
|
|
|
return -1;
|
|
|
|
} else {
|
2002-10-13 05:39:26 +00:00
|
|
|
return player->get_param(c, d);
|
2002-11-10 18:34:57 +00:00
|
|
|
}
|
2001-12-01 17:23:50 +00:00
|
|
|
case 1:
|
2002-10-14 16:42:57 +00:00
|
|
|
if (g_scumm->_gameId == GID_SAMNMAX)
|
2002-10-12 04:57:49 +00:00
|
|
|
player->jump (d - 1, (e - 1) * 4 + f, ((g * player->_ticks_per_beat) >> 2) + h);
|
|
|
|
else
|
|
|
|
player->set_priority(c);
|
2001-12-01 17:23:50 +00:00
|
|
|
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:
|
2002-04-11 17:19:16 +00:00
|
|
|
return player->jump(c, d, e) ? 0 : -1;
|
2001-12-01 17:23:50 +00:00
|
|
|
case 8:
|
2002-04-11 17:19:16 +00:00
|
|
|
return player->scan(c, d, e);
|
2001-12-01 17:23:50 +00:00
|
|
|
case 9:
|
2002-04-11 17:19:16 +00:00
|
|
|
return player->set_loop(c, d, e, f, g) ? 0 : -1;
|
2001-12-01 17:23:50 +00:00
|
|
|
case 10:
|
|
|
|
player->clear_loop();
|
|
|
|
return 0;
|
|
|
|
case 11:
|
2002-04-11 17:19:16 +00:00
|
|
|
((Part *)player)->set_onoff(d != 0);
|
2001-12-01 17:23:50 +00:00
|
|
|
return 0;
|
|
|
|
case 12:
|
|
|
|
return player->_hook.set(c, d, e);
|
|
|
|
case 13:
|
2002-04-11 17:19:16 +00:00
|
|
|
return player->fade_vol(c, d);
|
2001-12-01 17:23:50 +00:00
|
|
|
case 14:
|
2002-04-11 17:19:16 +00:00
|
|
|
return enqueue_trigger(b, c);
|
2001-12-01 17:23:50 +00:00
|
|
|
case 15:
|
2002-04-11 17:19:16 +00:00
|
|
|
return enqueue_command(b, c, d, e, f, g, h);
|
2001-12-01 17:23:50 +00:00
|
|
|
case 16:
|
|
|
|
return clear_queue();
|
|
|
|
case 19:
|
2002-04-11 17:19:16 +00:00
|
|
|
return player->get_param(c, d);
|
2001-12-01 17:23:50 +00:00
|
|
|
case 20:
|
2002-04-11 17:19:16 +00:00
|
|
|
return player->_hook.set(c, d, e);
|
2001-12-01 17:23:50 +00:00
|
|
|
case 21:
|
|
|
|
return -1;
|
|
|
|
case 22:
|
2002-04-11 17:19:16 +00:00
|
|
|
((Part *)player)->set_vol(d);
|
2001-12-01 17:23:50 +00:00
|
|
|
return 0;
|
|
|
|
case 23:
|
|
|
|
return query_queue(b);
|
|
|
|
case 24:
|
|
|
|
return 0;
|
|
|
|
default:
|
2003-03-18 16:13:52 +00:00
|
|
|
warning("doCommand (%d [%d/%d], %d, %d, %d, %d, %d, %d, %d) unsupported", a, param, cmd, b, c, d, e, f, g, h);
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2002-11-23 16:15:33 +00:00
|
|
|
int32 IMuseInternal::ImSetTrigger (int sound, int id, int a, int b, int c, int d) {
|
|
|
|
// Sam & Max: ImSetTrigger.
|
|
|
|
// Sets a trigger for a particular player and
|
2003-03-18 16:13:52 +00:00
|
|
|
// marker ID, along with doCommand parameters
|
2002-11-23 16:15:33 +00:00
|
|
|
// to invoke at the marker. The marker is
|
|
|
|
// represented by MIDI SysEx block 00 xx (F7)
|
|
|
|
// where "xx" is the marker ID.
|
|
|
|
uint16 oldest_trigger = 0;
|
2002-12-14 02:51:37 +00:00
|
|
|
ImTrigger *oldest_ptr = NULL;
|
2002-11-23 16:15:33 +00:00
|
|
|
|
|
|
|
int i;
|
2002-12-14 02:51:37 +00:00
|
|
|
ImTrigger *trig = _snm_triggers;
|
|
|
|
for (i = ARRAYSIZE (_snm_triggers); i; --i, ++trig) {
|
2002-11-23 16:15:33 +00:00
|
|
|
if (!trig->id)
|
|
|
|
break;
|
|
|
|
if (trig->id == id && trig->sound == sound)
|
|
|
|
break;
|
|
|
|
|
|
|
|
uint16 diff;
|
|
|
|
if (trig->expire <= _snm_trigger_index)
|
|
|
|
diff = _snm_trigger_index - trig->expire;
|
|
|
|
else
|
|
|
|
diff = 0x10000 - trig->expire + _snm_trigger_index;
|
|
|
|
|
2002-12-14 02:51:37 +00:00
|
|
|
if (!oldest_ptr || oldest_trigger < diff) {
|
|
|
|
oldest_ptr = trig;
|
2002-11-23 16:15:33 +00:00
|
|
|
oldest_trigger = diff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we didn't find a trigger, see if we can expire one.
|
2002-12-14 02:51:37 +00:00
|
|
|
if (!i) {
|
|
|
|
if (!oldest_ptr)
|
2002-11-23 16:15:33 +00:00
|
|
|
return -1;
|
2002-12-14 02:51:37 +00:00
|
|
|
trig = oldest_ptr;
|
2002-11-23 16:15:33 +00:00
|
|
|
}
|
|
|
|
|
2002-12-14 02:51:37 +00:00
|
|
|
trig->id = id;
|
|
|
|
trig->sound = sound;
|
|
|
|
trig->expire = (++_snm_trigger_index & 0xFFFF);
|
|
|
|
trig->command [0] = a;
|
|
|
|
trig->command [1] = b;
|
|
|
|
trig->command [2] = c;
|
|
|
|
trig->command [3] = d;
|
|
|
|
|
|
|
|
// If the command is to start a sound, stop that sound if it's already playing.
|
|
|
|
// This fixes some carnival music problems.
|
2003-03-23 13:14:54 +00:00
|
|
|
if (trig->command [0] == 8 && getSoundStatus (trig->command [1]))
|
2003-03-18 16:13:52 +00:00
|
|
|
stopSound (trig->command [1]);
|
2002-11-23 16:15:33 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int32 IMuseInternal::ImClearTrigger (int sound, int id) {
|
|
|
|
int count = 0;
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 16; ++i) {
|
|
|
|
if (_snm_triggers [i].sound == sound && _snm_triggers [i].id &&
|
|
|
|
(id == -1 || _snm_triggers [i].id == id))
|
|
|
|
{
|
|
|
|
_snm_triggers [i].sound = _snm_triggers [i].id = 0;
|
|
|
|
++count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return (count > 0) ? 0 : -1;
|
|
|
|
}
|
|
|
|
|
2002-04-29 11:48:33 +00:00
|
|
|
int IMuseInternal::set_channel_volume(uint chan, uint vol)
|
2002-04-11 17:19:16 +00:00
|
|
|
{
|
|
|
|
if (chan >= 8 || vol > 127)
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
_channel_volume[chan] = vol;
|
2002-11-17 17:59:00 +00:00
|
|
|
_channel_volume_eff[chan] = _master_volume * _music_volume * vol / 255 / 255;
|
2001-12-01 17:23:50 +00:00
|
|
|
update_volumes();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::update_volumes() {
|
2001-12-01 17:23:50 +00:00
|
|
|
Player *player;
|
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (player->_active)
|
|
|
|
player->set_vol(player->_volume);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::set_volchan_entry(uint a, uint b) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (a >= 8)
|
|
|
|
return -1;
|
|
|
|
_volchan_table[a] = b;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int HookDatas::query_param(int param, byte chan) {
|
2002-04-11 17:19:16 +00:00
|
|
|
switch (param) {
|
2001-12-01 17:23:50 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int HookDatas::set(byte cls, byte value, byte chan) {
|
2002-04-11 17:19:16 +00:00
|
|
|
switch (cls) {
|
2001-12-01 17:23:50 +00:00
|
|
|
case 0:
|
|
|
|
_jump = value;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
_transpose = value;
|
|
|
|
break;
|
|
|
|
case 2:
|
2002-04-11 17:19:16 +00:00
|
|
|
if (chan < 16)
|
2001-12-01 17:23:50 +00:00
|
|
|
_part_onoff[chan] = value;
|
2002-04-11 17:19:16 +00:00
|
|
|
else if (chan == 16)
|
2001-12-01 17:23:50 +00:00
|
|
|
memset(_part_onoff, value, 16);
|
|
|
|
break;
|
|
|
|
case 3:
|
2002-04-11 17:19:16 +00:00
|
|
|
if (chan < 16)
|
2001-12-01 17:23:50 +00:00
|
|
|
_part_volume[chan] = value;
|
2002-04-11 17:19:16 +00:00
|
|
|
else if (chan == 16)
|
2001-12-01 17:23:50 +00:00
|
|
|
memset(_part_volume, value, 16);
|
|
|
|
break;
|
|
|
|
case 4:
|
2002-04-11 17:19:16 +00:00
|
|
|
if (chan < 16)
|
2001-12-01 17:23:50 +00:00
|
|
|
_part_program[chan] = value;
|
2002-04-11 17:19:16 +00:00
|
|
|
else if (chan == 16)
|
2001-12-01 17:23:50 +00:00
|
|
|
memset(_part_program, value, 16);
|
|
|
|
break;
|
|
|
|
case 5:
|
2002-04-11 17:19:16 +00:00
|
|
|
if (chan < 16)
|
2001-12-01 17:23:50 +00:00
|
|
|
_part_transpose[chan] = value;
|
2002-04-11 17:19:16 +00:00
|
|
|
else if (chan == 16)
|
2001-12-01 17:23:50 +00:00
|
|
|
memset(_part_transpose, value, 16);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
VolumeFader *IMuseInternal::allocate_volume_fader() {
|
2001-12-01 17:23:50 +00:00
|
|
|
VolumeFader *vf;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
vf = _volume_fader;
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_volume_fader); vf->active;) {
|
2001-12-01 17:23:50 +00:00
|
|
|
vf++;
|
|
|
|
if (!--i)
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
vf->active = true;
|
|
|
|
_active_volume_faders = true;
|
|
|
|
return vf;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
Player *IMuseInternal::get_player_byid(int id) {
|
2001-12-01 17:23:50 +00:00
|
|
|
int i;
|
2002-04-11 17:19:16 +00:00
|
|
|
Player *player, *found = NULL;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
|
|
|
|
if (player->_active && player->_id == (uint16)id) {
|
|
|
|
if (found)
|
2001-12-01 17:23:50 +00:00
|
|
|
return NULL;
|
|
|
|
found = player;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::get_volchan_entry(uint a) {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (a < 8)
|
2001-12-01 17:23:50 +00:00
|
|
|
return _volchan_table[a];
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
uint32 IMuseInternal::property(int prop, uint32 value) {
|
2002-07-07 18:04:03 +00:00
|
|
|
switch (prop) {
|
2002-04-29 11:48:33 +00:00
|
|
|
case IMuse::PROP_TEMPO_BASE:
|
2002-04-14 18:13:08 +00:00
|
|
|
_game_tempo = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::setBase(byte **base) {
|
2002-04-14 18:13:08 +00:00
|
|
|
_base_sounds = base;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
IMuseInternal *IMuseInternal::create(OSystem *syst, MidiDriver *midi, SoundMixer *mixer) {
|
2002-04-29 11:48:33 +00:00
|
|
|
IMuseInternal *i = new IMuseInternal;
|
2002-04-14 18:13:08 +00:00
|
|
|
i->initialize(syst, midi, mixer);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::initialize(OSystem *syst, MidiDriver *midi, SoundMixer *mixer) {
|
2001-12-01 17:23:50 +00:00
|
|
|
int i;
|
2002-04-14 18:13:08 +00:00
|
|
|
IMuseDriver *driv;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-12-18 13:22:40 +00:00
|
|
|
if (midi == NULL)
|
2002-11-26 16:54:58 +00:00
|
|
|
driv = NULL;
|
2002-12-18 13:22:40 +00:00
|
|
|
else
|
|
|
|
driv = new IMuseDriver (midi);
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
_driver = driv;
|
|
|
|
_hardware_type = driv->get_hardware_type();
|
|
|
|
_game_tempo = driv->get_base_tempo();
|
2002-07-07 18:04:03 +00:00
|
|
|
|
|
|
|
driv->init(this, syst);
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2002-11-17 17:59:00 +00:00
|
|
|
_master_volume = 255;
|
2002-04-11 17:19:16 +00:00
|
|
|
if (_music_volume < 1)
|
2002-07-28 15:03:45 +00:00
|
|
|
_music_volume = kDefaultMusicVolume;
|
2002-03-05 23:37:31 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = 0; i != 8; i++)
|
2001-12-01 17:23:50 +00:00
|
|
|
_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;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::init_queue() {
|
2001-12-01 17:23:50 +00:00
|
|
|
_queue_adding = false;
|
|
|
|
_queue_pos = 0;
|
|
|
|
_queue_end = 0;
|
|
|
|
_trigger_count = 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::pause(bool paused) {
|
2002-11-17 17:59:00 +00:00
|
|
|
int vol = _music_volume;
|
|
|
|
if (paused)
|
|
|
|
_music_volume = 0;
|
|
|
|
update_volumes();
|
|
|
|
_music_volume = vol;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
_paused = paused;
|
|
|
|
}
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Player implementation
|
|
|
|
//
|
|
|
|
////////////////////////////////////////
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int Player::fade_vol(byte vol, int time) {
|
2001-12-01 17:23:50 +00:00
|
|
|
VolumeFader *vf;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
cancel_volume_fade();
|
2002-04-11 17:19:16 +00:00
|
|
|
if (time == 0) {
|
2001-12-01 17:23:50 +00:00
|
|
|
set_vol(vol);
|
|
|
|
return 0;
|
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
vf = _se->allocate_volume_fader();
|
2002-04-11 17:19:16 +00:00
|
|
|
if (vf == NULL)
|
2001-12-01 17:23:50 +00:00
|
|
|
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;
|
2002-04-11 17:19:16 +00:00
|
|
|
if (i < 0) {
|
2001-12-01 17:23:50 +00:00
|
|
|
i = -i;
|
|
|
|
vf->direction = -1;
|
|
|
|
} else {
|
|
|
|
vf->direction = 1;
|
|
|
|
}
|
|
|
|
vf->speed_lo = i % time;
|
|
|
|
vf->speed_lo_counter = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
bool Player::is_fading_out() {
|
2002-09-11 13:28:34 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::clear() {
|
2001-12-01 17:23:50 +00:00
|
|
|
uninit_seq();
|
|
|
|
cancel_volume_fade();
|
|
|
|
uninit_parts();
|
|
|
|
_active = false;
|
|
|
|
_ticks_per_beat = TICKS_PER_BEAT;
|
|
|
|
}
|
|
|
|
|
2003-03-18 16:13:52 +00:00
|
|
|
bool Player::startSound(int sound) {
|
2001-12-01 17:23:50 +00:00
|
|
|
void *mdhd;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
mdhd = _se->findTag(sound, MDHD_TAG, 0);
|
2002-04-11 17:19:16 +00:00
|
|
|
if (mdhd == NULL) {
|
2002-03-11 04:37:06 +00:00
|
|
|
mdhd = _se->findTag(sound, MDPG_TAG, 0);
|
2002-04-11 17:19:16 +00:00
|
|
|
if (mdhd == NULL) {
|
2003-03-18 16:13:52 +00:00
|
|
|
warning("P::startSound failed: Couldn't find %s", MDHD_TAG);
|
2002-08-25 15:06:53 +00:00
|
|
|
return false;
|
2002-03-11 04:37:06 +00:00
|
|
|
}
|
2002-03-05 16:13:09 +00:00
|
|
|
}
|
2002-07-22 12:44:57 +00:00
|
|
|
|
2002-05-04 00:20:39 +00:00
|
|
|
_mt32emulate = _se->isMT32(sound);
|
2002-09-29 11:11:42 +00:00
|
|
|
_isGM = _se->isGM(sound);
|
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
_parts = NULL;
|
|
|
|
_active = true;
|
|
|
|
_id = sound;
|
|
|
|
_priority = 0x80;
|
|
|
|
_volume = 0x7F;
|
|
|
|
_vol_chan = 0xFFFF;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
_vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
_pan = 0;
|
|
|
|
_transpose = 0;
|
|
|
|
_detune = 0;
|
|
|
|
|
|
|
|
hook_clear();
|
|
|
|
if (start_seq_sound(sound) != 0) {
|
|
|
|
_active = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::hook_clear() {
|
2001-12-01 17:23:50 +00:00
|
|
|
memset(&_hook, 0, sizeof(_hook));
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int Player::start_seq_sound(int sound) {
|
2001-12-01 17:23:50 +00:00
|
|
|
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);
|
2002-04-11 17:19:16 +00:00
|
|
|
if (ptr == NULL)
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
track_ptr = ptr;
|
|
|
|
_cur_pos = _next_pos = get_delta_time(&track_ptr);
|
|
|
|
_song_offset = track_ptr - ptr;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
_tick_index = _cur_pos;
|
|
|
|
_beat_index = 1;
|
|
|
|
|
|
|
|
if (_tick_index >= _ticks_per_beat) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_beat_index += _tick_index / _ticks_per_beat;
|
2001-12-01 17:23:50 +00:00
|
|
|
_tick_index %= _ticks_per_beat;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::set_tempo(uint32 b) {
|
2002-04-11 17:19:16 +00:00
|
|
|
uint32 i, j;
|
2002-03-05 15:50:11 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
i = _se->_game_tempo;
|
2002-07-07 18:04:03 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
j = _tempo = b;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
while (i & 0xFFFF0000 || j & 0xFFFF0000) {
|
|
|
|
i >>= 1;
|
|
|
|
j >>= 1;
|
|
|
|
}
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
_tempo_eff = (i << 16) / j;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
set_speed(_speed);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::cancel_volume_fade() {
|
2001-12-01 17:23:50 +00:00
|
|
|
VolumeFader *vf = _se->_volume_fader;
|
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = 0; i < 8; i++, vf++) {
|
|
|
|
if (vf->active && vf->player == this)
|
2001-12-01 17:23:50 +00:00
|
|
|
vf->active = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::uninit_parts() {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (_parts && _parts->_player != this)
|
|
|
|
error("asd");
|
2002-04-11 17:19:16 +00:00
|
|
|
while (_parts)
|
2001-12-01 17:23:50 +00:00
|
|
|
_parts->uninit();
|
2002-12-01 20:16:54 +00:00
|
|
|
_se->_driver->update_pris(); // In case another player couldn't allocate all its parts
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::uninit_seq() {
|
2001-12-01 17:23:50 +00:00
|
|
|
_abort = true;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::set_speed(byte speed) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_speed = speed;
|
|
|
|
_timer_speed = (_tempo_eff * speed >> 7);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
byte *Player::parse_midi(byte *s) {
|
2002-04-11 17:19:16 +00:00
|
|
|
byte cmd, chan, note, velocity, control;
|
2001-12-01 17:23:50 +00:00
|
|
|
uint value;
|
|
|
|
Part *part;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
cmd = *s++;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
chan = cmd & 0xF;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
switch (cmd >> 4) {
|
2002-12-15 01:55:27 +00:00
|
|
|
case 0x8: // Key Off
|
2001-12-01 17:23:50 +00:00
|
|
|
note = *s++;
|
|
|
|
if (!_scanning) {
|
|
|
|
key_off(chan, note);
|
|
|
|
} else {
|
|
|
|
clear_active_note(chan, note);
|
|
|
|
}
|
2002-12-15 01:55:27 +00:00
|
|
|
s++; // Skip velocity
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 0x9: // Key On
|
2001-12-01 17:23:50 +00:00
|
|
|
note = *s++;
|
|
|
|
velocity = *s++;
|
|
|
|
if (velocity) {
|
|
|
|
if (!_scanning)
|
2002-04-11 17:19:16 +00:00
|
|
|
key_on(chan, note, velocity);
|
2001-12-01 17:23:50 +00:00
|
|
|
else
|
2002-04-11 17:19:16 +00:00
|
|
|
set_active_note(chan, note);
|
2001-12-01 17:23:50 +00:00
|
|
|
} else {
|
|
|
|
if (!_scanning)
|
|
|
|
key_off(chan, note);
|
|
|
|
else
|
2002-04-11 17:19:16 +00:00
|
|
|
clear_active_note(chan, note);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 0xA: // Aftertouch
|
2001-12-01 17:23:50 +00:00
|
|
|
s += 2;
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 0xB: // Control Change
|
2001-12-01 17:23:50 +00:00
|
|
|
control = *s++;
|
|
|
|
value = *s++;
|
|
|
|
part = get_part(chan);
|
|
|
|
if (!part)
|
|
|
|
break;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
|
|
|
switch (control) {
|
2002-12-15 01:55:27 +00:00
|
|
|
case 1: // Modulation Wheel
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_modwheel(value);
|
|
|
|
break;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 7: // Volume
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_vol(value);
|
|
|
|
break;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 10: // Pan Position
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_pan(value - 0x40);
|
|
|
|
break;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 16: // Pitchbend Factor (non-standard)
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_pitchbend_factor(value);
|
|
|
|
break;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 17: // GP Slider 2
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_detune(value - 0x40);
|
|
|
|
break;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 18: // GP Slider 3
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_pri(value - 0x40);
|
|
|
|
_se->_driver->update_pris();
|
|
|
|
break;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 64: // Sustain Pedal
|
2002-04-11 17:19:16 +00:00
|
|
|
part->set_pedal(value != 0);
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 91: // Effects Level
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_effect_level(value);
|
|
|
|
break;
|
2002-12-15 01:55:27 +00:00
|
|
|
case 93: // Chorus Level
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_chorus(value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
warning("parse_midi: invalid control %d", control);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 0xC: // Program Change
|
2001-12-01 17:23:50 +00:00
|
|
|
value = *s++;
|
|
|
|
part = get_part(chan);
|
2002-12-20 13:09:01 +00:00
|
|
|
if (part) {
|
2003-03-25 00:28:09 +00:00
|
|
|
if (_isGM) {
|
|
|
|
if (value < 128)
|
|
|
|
part->set_program(value);
|
|
|
|
} else {
|
|
|
|
if (value < 32)
|
|
|
|
part->load_global_instrument(value);
|
|
|
|
}
|
2002-12-20 13:09:01 +00:00
|
|
|
}
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 0xD: // Channel Pressure
|
2001-12-01 17:23:50 +00:00
|
|
|
s++;
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 0xE: // Pitch Bend
|
2001-12-01 17:23:50 +00:00
|
|
|
part = get_part(chan);
|
|
|
|
if (part)
|
2002-10-13 06:07:39 +00:00
|
|
|
part->set_pitchbend(((s[1] << 7) | s[0]) - 0x2000);
|
2002-04-11 17:19:16 +00:00
|
|
|
s += 2;
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 0xF:
|
2002-04-11 17:19:16 +00:00
|
|
|
if (chan == 0) {
|
2001-12-01 17:23:50 +00:00
|
|
|
uint size = get_delta_time(&s);
|
2002-11-06 21:34:38 +00:00
|
|
|
parse_sysex(s, size);
|
2001-12-01 17:23:50 +00:00
|
|
|
s += size;
|
2002-04-11 17:19:16 +00:00
|
|
|
} else if (chan == 0xF) {
|
2001-12-01 17:23:50 +00:00
|
|
|
cmd = *s++;
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd == 47)
|
2002-12-15 01:55:27 +00:00
|
|
|
goto Error; // End of song
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd == 81) {
|
|
|
|
set_tempo((s[1] << 16) | (s[2] << 8) | s[3]);
|
|
|
|
s += 4;
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
s += get_delta_time(&s);
|
2002-04-11 17:19:16 +00:00
|
|
|
} else if (chan == 0x7) {
|
2001-12-01 17:23:50 +00:00
|
|
|
s += get_delta_time(&s);
|
|
|
|
} else {
|
|
|
|
goto Error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2002-04-11 17:19:16 +00:00
|
|
|
Error:;
|
|
|
|
if (!_scanning)
|
2001-12-01 17:23:50 +00:00
|
|
|
clear();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::parse_sysex(byte *p, uint len) {
|
2001-12-01 17:23:50 +00:00
|
|
|
byte code;
|
|
|
|
byte a;
|
|
|
|
uint b;
|
|
|
|
byte buf[128];
|
|
|
|
Part *part;
|
|
|
|
|
2002-11-06 21:34:38 +00:00
|
|
|
// Check SysEx manufacturer.
|
|
|
|
// Roland is 0x41
|
|
|
|
a = *p++;
|
|
|
|
--len;
|
|
|
|
if (a != IMUSE_SYSEX_ID) {
|
|
|
|
if (a == ROLAND_SYSEX_ID) {
|
|
|
|
// Roland custom instrument definition.
|
|
|
|
part = get_part (p[0] & 0x0F);
|
|
|
|
if (part) {
|
2002-12-18 13:22:40 +00:00
|
|
|
part->_instrument.roland (p - 1);
|
|
|
|
part->changed (IMuseDriver::pcProgram);
|
2002-11-06 21:34:38 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warning ("Unknown SysEx manufacturer 0x%02X", (int) a);
|
|
|
|
}
|
2002-12-18 09:27:46 +00:00
|
|
|
return;
|
2002-11-06 21:34:38 +00:00
|
|
|
}
|
|
|
|
--len;
|
|
|
|
|
|
|
|
// Too big?
|
2002-04-11 17:19:16 +00:00
|
|
|
if (len >= sizeof(buf) * 2)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
switch (code = *p++) {
|
2002-10-10 15:06:02 +00:00
|
|
|
case 0:
|
2002-10-14 16:42:57 +00:00
|
|
|
if (g_scumm->_gameId != GID_SAMNMAX) {
|
2002-11-06 21:34:38 +00:00
|
|
|
// There are 17 bytes of useful information beyond
|
|
|
|
// what we've read so far. All we know about them is
|
|
|
|
// as follows:
|
|
|
|
// BYTE 00: Channel #
|
|
|
|
// BYTE 02: BIT 01 (0x01): Part on? (1 = yes)
|
|
|
|
// BYTE 05: Volume (upper 4 bits) [guessing]
|
|
|
|
// BYTE 06: Volume (lower 4 bits) [guessing]
|
|
|
|
// BYTE 09: BIT 04 (0x08): Percussion? (1 = yes)
|
|
|
|
part = get_part (p[0] & 0x0F);
|
2002-10-10 15:06:02 +00:00
|
|
|
if (part) {
|
2002-11-06 21:34:38 +00:00
|
|
|
part->set_onoff (p[2] & 0x01);
|
|
|
|
part->set_vol ((p[5] & 0x0F) << 4 | (p[6] & 0x0F));
|
2002-11-21 19:06:42 +00:00
|
|
|
part->_percussion = _isGM ? ((p[9] & 0x08) > 0) : false;
|
2002-11-06 21:34:38 +00:00
|
|
|
if (part->_percussion) {
|
2002-12-18 17:14:05 +00:00
|
|
|
if (part->_mc) {
|
2002-11-06 21:34:38 +00:00
|
|
|
part->off();
|
2002-12-18 17:14:05 +00:00
|
|
|
part->update_pris();
|
|
|
|
}
|
2002-11-06 21:34:38 +00:00
|
|
|
} else {
|
|
|
|
part->changed (IMuseDriver::pcAll);
|
|
|
|
}
|
2002-10-10 15:06:02 +00:00
|
|
|
}
|
2002-10-12 04:57:49 +00:00
|
|
|
} else {
|
2002-12-15 01:55:27 +00:00
|
|
|
// Sam & Max: Trigger Event
|
2003-03-18 16:13:52 +00:00
|
|
|
// Triggers are set by doCommand (ImSetTrigger).
|
2002-12-15 01:55:27 +00:00
|
|
|
// When a SysEx marker is encountered whose sound
|
|
|
|
// ID and marker ID match what was set by ImSetTrigger,
|
|
|
|
// something magical is supposed to happen....
|
2002-10-14 16:42:57 +00:00
|
|
|
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;
|
2003-03-18 16:13:52 +00:00
|
|
|
_se->doCommand (_se->_snm_triggers [a].command [0],
|
2002-10-14 16:42:57 +00:00
|
|
|
_se->_snm_triggers [a].command [1],
|
|
|
|
_se->_snm_triggers [a].command [2],
|
|
|
|
_se->_snm_triggers [a].command [3],
|
|
|
|
0, 0, 0, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2002-10-10 15:06:02 +00:00
|
|
|
} // end if
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
2002-10-14 16:42:57 +00:00
|
|
|
// This SysEx is used in Sam & Max for maybe_jump.
|
2002-10-10 15:06:02 +00:00
|
|
|
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]);
|
2002-09-09 05:03:55 +00:00
|
|
|
break;
|
2002-12-05 21:45:55 +00:00
|
|
|
|
|
|
|
case 2: // Start of song. Ignore for now.
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 16: // Adlib instrument definition (Part)
|
2001-12-01 17:23:50 +00:00
|
|
|
a = *p++ & 0x0F;
|
2002-11-21 19:06:42 +00:00
|
|
|
if (_se->_hardware_type != *p++ && false)
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
decode_sysex_bytes(p, buf, len - 3);
|
|
|
|
part = get_part(a);
|
2002-04-11 17:19:16 +00:00
|
|
|
if (part)
|
2002-12-18 13:22:40 +00:00
|
|
|
part->set_instrument((byte *) buf);
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 17: // Adlib instrument definition (Global)
|
2001-12-01 17:23:50 +00:00
|
|
|
p++;
|
2002-11-21 19:06:42 +00:00
|
|
|
if (_se->_hardware_type != *p++ && false)
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
2002-04-11 17:19:16 +00:00
|
|
|
a = *p++;
|
2001-12-01 17:23:50 +00:00
|
|
|
decode_sysex_bytes(p, buf, len - 4);
|
|
|
|
_se->_driver->set_instrument(a, buf);
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 33: // Parameter adjust
|
2001-12-01 17:23:50 +00:00
|
|
|
a = *p++ & 0x0F;
|
2002-12-01 15:51:29 +00:00
|
|
|
if (_se->_hardware_type != *p++ && false)
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
decode_sysex_bytes(p, buf, len - 3);
|
|
|
|
part = get_part(a);
|
|
|
|
if (part)
|
2002-04-11 17:19:16 +00:00
|
|
|
part->set_param(read_word(buf), read_word(buf + 2));
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 48: // Hook - jump
|
2001-12-01 17:23:50 +00:00
|
|
|
if (_scanning)
|
|
|
|
break;
|
2002-04-11 17:19:16 +00:00
|
|
|
decode_sysex_bytes(p + 1, buf, len - 2);
|
2002-10-10 15:06:02 +00:00
|
|
|
maybe_jump (buf[0], read_word (buf + 1), read_word (buf + 3), read_word (buf + 5));
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 49: // Hook - global transpose
|
2002-04-11 17:19:16 +00:00
|
|
|
decode_sysex_bytes(p + 1, buf, len - 2);
|
2001-12-01 17:23:50 +00:00
|
|
|
maybe_set_transpose(buf);
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 50: // Hook - part on/off
|
2001-12-01 17:23:50 +00:00
|
|
|
buf[0] = *p++ & 0x0F;
|
2002-04-11 17:19:16 +00:00
|
|
|
decode_sysex_bytes(p, buf + 1, len - 2);
|
2001-12-01 17:23:50 +00:00
|
|
|
maybe_part_onoff(buf);
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 51: // Hook - set volume
|
2001-12-01 17:23:50 +00:00
|
|
|
buf[0] = *p++ & 0x0F;
|
2002-04-11 17:19:16 +00:00
|
|
|
decode_sysex_bytes(p, buf + 1, len - 2);
|
2001-12-01 17:23:50 +00:00
|
|
|
maybe_set_volume(buf);
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 52: // Hook - set program
|
2001-12-01 17:23:50 +00:00
|
|
|
buf[0] = *p++ & 0x0F;
|
2002-04-11 17:19:16 +00:00
|
|
|
decode_sysex_bytes(p, buf + 1, len - 2);
|
2001-12-01 17:23:50 +00:00
|
|
|
maybe_set_program(buf);
|
|
|
|
break;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 53: // Hook - set transpose
|
2001-12-01 17:23:50 +00:00
|
|
|
buf[0] = *p++ & 0x0F;
|
2002-04-11 17:19:16 +00:00
|
|
|
decode_sysex_bytes(p, buf + 1, len - 2);
|
2001-12-01 17:23:50 +00:00
|
|
|
maybe_set_transpose_part(buf);
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 64: // Marker
|
2001-12-01 17:23:50 +00:00
|
|
|
p++;
|
|
|
|
len -= 2;
|
|
|
|
while (len--) {
|
|
|
|
_se->handle_marker(_id, *p++);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 80: // Loop
|
2002-04-11 17:19:16 +00:00
|
|
|
decode_sysex_bytes(p + 1, buf, len - 2);
|
|
|
|
set_loop(read_word(buf),
|
2002-07-07 18:04:03 +00:00
|
|
|
read_word(buf + 2), read_word(buf + 4), read_word(buf + 6), read_word(buf + 8)
|
2002-04-11 17:19:16 +00:00
|
|
|
);
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 81: // End loop
|
2001-12-01 17:23:50 +00:00
|
|
|
clear_loop();
|
|
|
|
break;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
case 96: // Set instrument
|
2001-12-01 17:23:50 +00:00
|
|
|
part = get_part(p[0] & 0x0F);
|
2002-07-07 18:04:03 +00:00
|
|
|
b = (p[1] & 0x0F) << 12 | (p[2] & 0x0F) << 8 | (p[4] & 0x0F) << 4 | (p[4] & 0x0F);
|
2002-04-11 17:19:16 +00:00
|
|
|
if (part)
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_instrument(b);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2002-12-01 15:51:29 +00:00
|
|
|
warning ("Unknown SysEx command %d", (int) code);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::decode_sysex_bytes(byte *src, byte *dst, int len) {
|
2002-04-11 17:19:16 +00:00
|
|
|
while (len >= 0) {
|
|
|
|
*dst++ = (src[0] << 4) | (src[1] & 0xF);
|
2001-12-01 17:23:50 +00:00
|
|
|
src += 2;
|
|
|
|
len -= 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::maybe_jump (byte cmd, uint track, uint beat, uint tick) {
|
2002-12-15 01:55:27 +00:00
|
|
|
// Is this the hook I'm waiting for?
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd && _hook._jump != cmd)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Reset hook?
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd != 0 && cmd < 0x80)
|
2001-12-01 17:23:50 +00:00
|
|
|
_hook._jump = 0;
|
|
|
|
|
2002-10-10 15:06:02 +00:00
|
|
|
jump (track, beat, tick);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::maybe_set_transpose(byte *data) {
|
2001-12-01 17:23:50 +00:00
|
|
|
byte cmd;
|
|
|
|
|
|
|
|
cmd = data[0];
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Is this the hook I'm waiting for?
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd && _hook._transpose != cmd)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Reset hook?
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd != 0 && cmd < 0x80)
|
2001-12-01 17:23:50 +00:00
|
|
|
_hook._transpose = 0;
|
|
|
|
|
2002-07-07 18:04:03 +00:00
|
|
|
set_transpose(data[1], (int8)data[2]);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::maybe_part_onoff(byte *data) {
|
2002-04-11 17:19:16 +00:00
|
|
|
byte cmd, *p;
|
2001-12-01 17:23:50 +00:00
|
|
|
uint chan;
|
|
|
|
Part *part;
|
|
|
|
|
|
|
|
cmd = data[1];
|
|
|
|
chan = data[0];
|
|
|
|
|
|
|
|
p = &_hook._part_onoff[chan];
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Is this the hook I'm waiting for?
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd && *p != cmd)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd != 0 && cmd < 0x80)
|
2001-12-01 17:23:50 +00:00
|
|
|
*p = 0;
|
|
|
|
|
|
|
|
part = get_part(chan);
|
|
|
|
if (part)
|
2002-04-11 17:19:16 +00:00
|
|
|
part->set_onoff(data[2] != 0);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::maybe_set_volume(byte *data) {
|
2001-12-01 17:23:50 +00:00
|
|
|
byte cmd;
|
|
|
|
byte *p;
|
|
|
|
uint chan;
|
|
|
|
Part *part;
|
|
|
|
|
|
|
|
cmd = data[1];
|
|
|
|
chan = data[0];
|
|
|
|
|
|
|
|
p = &_hook._part_volume[chan];
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Is this the hook I'm waiting for?
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd && *p != cmd)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Reset hook?
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd != 0 && cmd < 0x80)
|
2001-12-01 17:23:50 +00:00
|
|
|
*p = 0;
|
|
|
|
|
|
|
|
part = get_part(chan);
|
|
|
|
if (part)
|
|
|
|
part->set_vol(data[2]);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::maybe_set_program(byte *data) {
|
2001-12-01 17:23:50 +00:00
|
|
|
byte cmd;
|
|
|
|
byte *p;
|
|
|
|
uint chan;
|
|
|
|
Part *part;
|
|
|
|
|
|
|
|
cmd = data[1];
|
|
|
|
chan = data[0];
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Is this the hook I'm waiting for?
|
2001-12-01 17:23:50 +00:00
|
|
|
p = &_hook._part_program[chan];
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd && *p != cmd)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd != 0 && cmd < 0x80)
|
2001-12-01 17:23:50 +00:00
|
|
|
*p = 0;
|
|
|
|
|
|
|
|
part = get_part(chan);
|
|
|
|
if (part)
|
|
|
|
part->set_program(data[2]);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::maybe_set_transpose_part(byte *data) {
|
2001-12-01 17:23:50 +00:00
|
|
|
byte cmd;
|
|
|
|
byte *p;
|
|
|
|
uint chan;
|
|
|
|
|
|
|
|
cmd = data[1];
|
|
|
|
chan = data[0];
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Is this the hook I'm waiting for?
|
2001-12-01 17:23:50 +00:00
|
|
|
p = &_hook._part_transpose[chan];
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd && *p != cmd)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Reset hook?
|
2002-04-11 17:19:16 +00:00
|
|
|
if (cmd != 0 && cmd < 0x80)
|
2001-12-01 17:23:50 +00:00
|
|
|
*p = 0;
|
|
|
|
|
2002-07-07 18:04:03 +00:00
|
|
|
part_set_transpose(chan, data[2], (int8)data[3]);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int Player::set_transpose(byte relative, int b) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (b > 24 || b < -24 || relative > 1)
|
2001-12-01 17:23:50 +00:00
|
|
|
return -1;
|
|
|
|
if (relative)
|
|
|
|
b = transpose_clamp(_transpose + b, -7, 7);
|
|
|
|
|
|
|
|
_transpose = b;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (part = _parts; part; part = part->_next) {
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_transpose(part->_transpose);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::clear_active_notes() {
|
2001-12-01 17:23:50 +00:00
|
|
|
memset(_se->_active_notes, 0, sizeof(_se->_active_notes));
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::clear_active_note(int chan, byte note) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_se->_active_notes[note] &= ~(1 << chan);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::set_active_note(int chan, byte note) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_se->_active_notes[note] |= (1 << chan);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::part_set_transpose(uint8 chan, byte relative, int8 b) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (b > 24 || b < -24)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
part = get_part(chan);
|
|
|
|
if (!part)
|
|
|
|
return;
|
|
|
|
if (relative)
|
|
|
|
b = transpose_clamp(b + part->_transpose, -7, 7);
|
|
|
|
part->set_transpose(b);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::key_on(uint8 chan, uint8 note, uint8 velocity) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
|
|
|
part = get_part(chan);
|
|
|
|
if (!part || !part->_on)
|
|
|
|
return;
|
|
|
|
|
|
|
|
part->key_on(note, velocity);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::key_off(uint8 chan, uint8 note) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (part = _parts; part; part = part->_next) {
|
|
|
|
if (part->_chan == (byte)chan && part->_on)
|
2001-12-01 17:23:50 +00:00
|
|
|
part->key_off(note);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
bool Player::jump(uint track, uint beat, uint tick) {
|
2001-12-01 17:23:50 +00:00
|
|
|
byte *mtrk, *cur_mtrk, *scanpos;
|
2002-04-11 17:19:16 +00:00
|
|
|
uint32 topos, curpos, track_offs;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
if (!_active)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
mtrk = _se->findTag(_song_index, "MTrk", track);
|
|
|
|
if (!mtrk)
|
|
|
|
return false;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
cur_mtrk = _se->findTag(_song_index, "MTrk", _track_index);
|
2001-12-01 17:23:50 +00:00
|
|
|
if (!cur_mtrk)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
_se->lock();
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (beat == 0)
|
|
|
|
beat = 1;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
topos = (beat - 1) * _ticks_per_beat + tick;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2002-07-07 18:04:03 +00:00
|
|
|
find_sustaining_notes(cur_mtrk + _song_offset, mtrk + track_offs, curpos - topos);
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
_beat_index = beat;
|
|
|
|
_tick_index = tick;
|
2003-04-18 14:38:31 +00:00
|
|
|
_cur_pos = curpos; // topos;
|
2001-12-01 17:23:50 +00:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
bool Player::set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick) {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (tobeat + 1 >= frombeat)
|
2001-12-01 17:23:50 +00:00
|
|
|
return false;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (tobeat == 0)
|
|
|
|
tobeat = 1;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
_loop_counter = 0; // Because of possible interrupts
|
2001-12-01 17:23:50 +00:00
|
|
|
_loop_to_beat = tobeat;
|
|
|
|
_loop_to_tick = totick;
|
|
|
|
_loop_from_beat = frombeat;
|
|
|
|
_loop_from_tick = fromtick;
|
|
|
|
_loop_counter = count;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::clear_loop() {
|
2001-12-01 17:23:50 +00:00
|
|
|
_loop_counter = 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::turn_off_pedals() {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (part = _parts; part; part = part->_next) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (part->_pedal)
|
|
|
|
part->set_pedal(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::find_sustaining_notes(byte *a, byte *b, uint32 l) {
|
2001-12-01 17:23:50 +00:00
|
|
|
uint32 pos;
|
|
|
|
uint16 mask;
|
|
|
|
uint16 *bitlist_ptr;
|
2002-04-11 17:19:16 +00:00
|
|
|
SustainingNotes *sn, *next;
|
2001-12-01 17:23:50 +00:00
|
|
|
IsNoteCmdData isnote;
|
|
|
|
int j;
|
|
|
|
uint num_active;
|
|
|
|
uint max_off_pos;
|
|
|
|
|
|
|
|
num_active = update_actives();
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// pos contains number of ticks since current position
|
2001-12-01 17:23:50 +00:00
|
|
|
pos = _next_pos - _cur_pos;
|
2002-07-07 18:04:03 +00:00
|
|
|
if ((int32)pos < 0)
|
2001-12-01 17:23:50 +00:00
|
|
|
pos = 0;
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Locate the positions where the notes are turned off.
|
|
|
|
// Remember each note that was turned off.
|
2001-12-01 17:23:50 +00:00
|
|
|
while (num_active != 0) {
|
2002-12-15 01:55:27 +00:00
|
|
|
// Is note off?
|
2002-04-11 17:19:16 +00:00
|
|
|
j = is_note_cmd(&a, &isnote);
|
|
|
|
if (j == -1)
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
if (j == 1) {
|
2002-04-11 17:19:16 +00:00
|
|
|
mask = 1 << isnote.chan;
|
2001-12-01 17:23:50 +00:00
|
|
|
bitlist_ptr = _se->_active_notes + isnote.note;
|
|
|
|
if (*bitlist_ptr & mask) {
|
|
|
|
*bitlist_ptr &= ~mask;
|
|
|
|
num_active--;
|
2002-12-15 01:55:27 +00:00
|
|
|
// Get a node from the free list
|
2002-04-11 17:19:16 +00:00
|
|
|
if ((sn = _se->_sustain_notes_free) == NULL)
|
2001-12-01 17:23:50 +00:00
|
|
|
return;
|
|
|
|
_se->_sustain_notes_free = sn->next;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Insert it in the beginning of the used list
|
2001-12-01 17:23:50 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Find the maximum position where a note was turned off
|
2001-12-01 17:23:50 +00:00
|
|
|
max_off_pos = 0;
|
2002-04-11 17:19:16 +00:00
|
|
|
for (sn = _se->_sustain_notes_used; sn; sn = sn->next) {
|
|
|
|
_se->_active_notes[sn->note] |= (1 << sn->chan);
|
2001-12-01 17:23:50 +00:00
|
|
|
if (sn->off_pos > max_off_pos) {
|
|
|
|
max_off_pos = sn->off_pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// locate positions where notes are turned on
|
2001-12-01 17:23:50 +00:00
|
|
|
pos = l;
|
|
|
|
while (pos < max_off_pos) {
|
2002-04-11 17:19:16 +00:00
|
|
|
j = is_note_cmd(&b, &isnote);
|
|
|
|
if (j == -1)
|
2001-12-01 17:23:50 +00:00
|
|
|
break;
|
|
|
|
if (j == 2) {
|
2002-04-11 17:19:16 +00:00
|
|
|
mask = 1 << isnote.chan;
|
2001-12-01 17:23:50 +00:00
|
|
|
bitlist_ptr = _se->_active_notes + isnote.note;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
if (*bitlist_ptr & mask) {
|
2001-12-01 17:23:50 +00:00
|
|
|
sn = _se->_sustain_notes_used;
|
|
|
|
while (sn) {
|
|
|
|
next = sn->next;
|
2002-07-07 18:04:03 +00:00
|
|
|
if (sn->note == isnote.note && sn->chan == isnote.chan && pos < sn->off_pos) {
|
2001-12-01 17:23:50 +00:00
|
|
|
*bitlist_ptr &= ~mask;
|
2002-12-15 01:55:27 +00:00
|
|
|
// Unlink from the sustain list
|
2001-12-01 17:23:50 +00:00
|
|
|
if (next)
|
|
|
|
next->prev = sn->prev;
|
|
|
|
if (sn->prev)
|
|
|
|
sn->prev->next = next;
|
|
|
|
else
|
|
|
|
_se->_sustain_notes_used = next;
|
2002-12-15 01:55:27 +00:00
|
|
|
// Insert into the free list
|
2001-12-01 17:23:50 +00:00
|
|
|
sn->next = _se->_sustain_notes_free;
|
|
|
|
_se->_sustain_notes_free = sn;
|
|
|
|
}
|
|
|
|
sn = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pos += get_delta_time(&b);
|
|
|
|
}
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Concatenate head and used list
|
2001-12-01 17:23:50 +00:00
|
|
|
if (!_se->_sustain_notes_head) {
|
|
|
|
_se->_sustain_notes_head = _se->_sustain_notes_used;
|
|
|
|
_se->_sustain_notes_used = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sn = _se->_sustain_notes_head;
|
2002-04-11 17:19:16 +00:00
|
|
|
while (sn->next)
|
|
|
|
sn = sn->next;
|
2001-12-01 17:23:50 +00:00
|
|
|
sn->next = _se->_sustain_notes_used;
|
|
|
|
_se->_sustain_notes_used = NULL;
|
|
|
|
if (sn->next)
|
|
|
|
sn->next->prev = sn;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
Part *Player::get_part(uint8 chan) {
|
2001-12-01 17:23:50 +00:00
|
|
|
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;
|
|
|
|
}
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
part->_chan = chan;
|
|
|
|
part->setup(this);
|
|
|
|
|
|
|
|
return part;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
uint Player::update_actives() {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
uint16 *active;
|
|
|
|
int count = 0;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
|
|
|
clear_active_notes();
|
2001-12-01 17:23:50 +00:00
|
|
|
active = _se->_active_notes;
|
2002-04-11 17:19:16 +00:00
|
|
|
for (part = _parts; part; part = part->_next) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (part->_mc)
|
|
|
|
count += part->update_actives(active);
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::set_priority(int pri) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
|
|
|
_priority = pri;
|
2002-04-11 17:19:16 +00:00
|
|
|
for (part = _parts; part; part = part->_next) {
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_pri(part->_pri);
|
|
|
|
}
|
|
|
|
_se->_driver->update_pris();
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::set_pan(int pan) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
|
|
|
_pan = pan;
|
2002-04-11 17:19:16 +00:00
|
|
|
for (part = _parts; part; part = part->_next) {
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_pan(part->_pan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::set_detune(int detune) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
|
|
|
_detune = detune;
|
2002-04-11 17:19:16 +00:00
|
|
|
for (part = _parts; part; part = part->_next) {
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_detune(part->_detune);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int Player::scan(uint totrack, uint tobeat, uint totick) {
|
2002-04-11 17:19:16 +00:00
|
|
|
byte *mtrk, *scanptr;
|
|
|
|
uint32 curpos, topos;
|
2001-12-01 17:23:50 +00:00
|
|
|
uint32 pos;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
assert(totrack >= 0 && tobeat >= 0 && totick >= 0);
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
if (!_active)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
mtrk = _se->findTag(_song_index, "MTrk", totrack);
|
|
|
|
if (!mtrk)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
_se->lock();
|
2002-04-11 17:19:16 +00:00
|
|
|
if (tobeat == 0)
|
2001-12-01 17:23:50 +00:00
|
|
|
tobeat++;
|
|
|
|
|
|
|
|
turn_off_parts();
|
|
|
|
clear_active_notes();
|
|
|
|
scanptr = mtrk;
|
|
|
|
curpos = get_delta_time(&scanptr);
|
2002-04-11 17:19:16 +00:00
|
|
|
_scanning = true;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
topos = (tobeat - 1) * _ticks_per_beat + totick;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
while (curpos < topos) {
|
|
|
|
scanptr = parse_midi(scanptr);
|
|
|
|
if (!scanptr) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_scanning = false;
|
2001-12-01 17:23:50 +00:00
|
|
|
_se->unlock();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
curpos += get_delta_time(&scanptr);
|
|
|
|
}
|
|
|
|
pos = scanptr - mtrk;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
_scanning = false;
|
2001-12-01 17:23:50 +00:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::turn_off_parts() {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (part = _parts; part; part = part->_next)
|
2001-12-01 17:23:50 +00:00
|
|
|
part->off();
|
2002-12-18 17:14:05 +00:00
|
|
|
_se->_driver->update_pris();
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::play_active_notes() {
|
2002-04-11 17:19:16 +00:00
|
|
|
int i, j;
|
2001-12-01 17:23:50 +00:00
|
|
|
uint mask;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = 0; i != 128; i++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
mask = _se->_active_notes[i];
|
2002-04-11 17:19:16 +00:00
|
|
|
for (j = 0; j != 16; j++, mask >>= 1) {
|
|
|
|
if (mask & 1) {
|
2001-12-01 17:23:50 +00:00
|
|
|
key_on(j, i, 80);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int Player::set_vol(byte vol) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
|
|
|
if (vol > 127)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
_volume = vol;
|
|
|
|
_vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (part = _parts; part; part = part->_next) {
|
2001-12-01 17:23:50 +00:00
|
|
|
part->set_vol(part->_vol);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int Player::get_param(int param, byte chan) {
|
2002-04-11 17:19:16 +00:00
|
|
|
switch (param) {
|
2001-12-01 17:23:50 +00:00
|
|
|
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;
|
2002-04-11 17:19:16 +00:00
|
|
|
case 14:
|
|
|
|
case 15:
|
|
|
|
case 16:
|
|
|
|
case 17:
|
2001-12-01 17:23:50 +00:00
|
|
|
return query_part_param(param, chan);
|
2002-04-11 17:19:16 +00:00
|
|
|
case 18:
|
|
|
|
case 19:
|
|
|
|
case 20:
|
|
|
|
case 21:
|
|
|
|
case 22:
|
|
|
|
case 23:
|
2001-12-01 17:23:50 +00:00
|
|
|
return _hook.query_param(param, chan);
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int Player::query_part_param(int param, byte chan) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
|
|
|
|
part = _parts;
|
|
|
|
while (part) {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (part->_chan == chan) {
|
|
|
|
switch (param) {
|
2001-12-01 17:23:50 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Player::sequencer_timer() {
|
2002-05-04 00:20:39 +00:00
|
|
|
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;
|
|
|
|
}
|
2002-07-07 18:04:03 +00:00
|
|
|
if (_loop_counter && _beat_index >= _loop_from_beat && _tick_index >= _loop_from_tick) {
|
2002-05-04 00:20:39 +00:00
|
|
|
_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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
////////////////////////////////////////////////////////////
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
enum {
|
|
|
|
TYPE_PART = 1,
|
2002-12-25 00:38:53 +00:00
|
|
|
TYPE_PLAYER = 2
|
2001-12-01 17:23:50 +00:00
|
|
|
};
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::saveReference(void *me_ref, byte type, void *ref) {
|
2002-12-08 16:14:29 +00:00
|
|
|
IMuseInternal *me = (IMuseInternal *)me_ref;
|
2002-04-11 17:19:16 +00:00
|
|
|
switch (type) {
|
|
|
|
case TYPE_PART:
|
|
|
|
return (Part *)ref - me->_parts;
|
|
|
|
case TYPE_PLAYER:
|
|
|
|
return (Player *)ref - me->_players;
|
2001-12-01 17:23:50 +00:00
|
|
|
default:
|
|
|
|
error("saveReference: invalid type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void *IMuseInternal::loadReference(void *me_ref, byte type, int ref) {
|
2002-12-08 16:14:29 +00:00
|
|
|
IMuseInternal *me = (IMuseInternal *)me_ref;
|
2002-04-11 17:19:16 +00:00
|
|
|
switch (type) {
|
|
|
|
case TYPE_PART:
|
|
|
|
return &me->_parts[ref];
|
|
|
|
case TYPE_PLAYER:
|
|
|
|
return &me->_players[ref];
|
2001-12-01 17:23:50 +00:00
|
|
|
default:
|
|
|
|
error("loadReference: invalid type");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseInternal::save_or_load(Serializer *ser, Scumm *scumm) {
|
2001-12-01 17:23:50 +00:00
|
|
|
const SaveLoadEntry mainEntries[] = {
|
2002-12-08 16:14:29 +00:00
|
|
|
MKLINE(IMuseInternal, _queue_end, sleUint8, VER_V8),
|
|
|
|
MKLINE(IMuseInternal, _queue_pos, sleUint8, VER_V8),
|
|
|
|
MKLINE(IMuseInternal, _queue_sound, sleUint16, VER_V8),
|
|
|
|
MKLINE(IMuseInternal, _queue_adding, sleByte, VER_V8),
|
|
|
|
MKLINE(IMuseInternal, _queue_marker, sleByte, VER_V8),
|
|
|
|
MKLINE(IMuseInternal, _queue_cleared, sleByte, VER_V8),
|
|
|
|
MKLINE(IMuseInternal, _master_volume, sleByte, VER_V8),
|
|
|
|
MKLINE(IMuseInternal, _trigger_count, sleUint16, VER_V8),
|
|
|
|
MKARRAY(IMuseInternal, _channel_volume[0], sleUint16, 8, VER_V8),
|
|
|
|
MKARRAY(IMuseInternal, _volchan_table[0], sleUint16, 8, VER_V8),
|
2001-12-01 17:23:50 +00:00
|
|
|
MKEND()
|
|
|
|
};
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
const SaveLoadEntry playerEntries[] = {
|
2002-12-08 16:14:29 +00:00
|
|
|
MKREF(Player, _parts, TYPE_PART, VER_V8),
|
|
|
|
MKLINE(Player, _active, sleByte, VER_V8),
|
|
|
|
MKLINE(Player, _id, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _priority, sleByte, VER_V8),
|
|
|
|
MKLINE(Player, _volume, sleByte, VER_V8),
|
|
|
|
MKLINE(Player, _pan, sleInt8, VER_V8),
|
|
|
|
MKLINE(Player, _transpose, sleByte, VER_V8),
|
|
|
|
MKLINE(Player, _detune, sleInt8, VER_V8),
|
|
|
|
MKLINE(Player, _vol_chan, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _vol_eff, sleByte, VER_V8),
|
|
|
|
MKLINE(Player, _speed, sleByte, VER_V8),
|
|
|
|
MKLINE(Player, _song_index, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _track_index, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _timer_counter, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _loop_to_beat, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _loop_from_beat, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _loop_counter, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _loop_to_tick, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _loop_from_tick, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _tempo, sleUint32, VER_V8),
|
|
|
|
MKLINE(Player, _cur_pos, sleUint32, VER_V8),
|
|
|
|
MKLINE(Player, _next_pos, sleUint32, VER_V8),
|
|
|
|
MKLINE(Player, _song_offset, sleUint32, VER_V8),
|
|
|
|
MKLINE(Player, _tick_index, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _beat_index, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _ticks_per_beat, sleUint16, VER_V8),
|
|
|
|
MKLINE(Player, _hook._jump, sleByte, VER_V8),
|
|
|
|
MKLINE(Player, _hook._transpose, sleByte, VER_V8),
|
|
|
|
MKARRAY(Player, _hook._part_onoff[0], sleByte, 16, VER_V8),
|
|
|
|
MKARRAY(Player, _hook._part_volume[0], sleByte, 16, VER_V8),
|
|
|
|
MKARRAY(Player, _hook._part_program[0], sleByte, 16, VER_V8),
|
|
|
|
MKARRAY(Player, _hook._part_transpose[0], sleByte, 16, VER_V8),
|
2001-12-01 17:23:50 +00:00
|
|
|
MKEND()
|
|
|
|
};
|
|
|
|
|
|
|
|
const SaveLoadEntry volumeFaderEntries[] = {
|
2002-12-08 16:14:29 +00:00
|
|
|
MKREF(VolumeFader, player, TYPE_PLAYER, VER_V8),
|
|
|
|
MKLINE(VolumeFader, active, sleUint8, VER_V8),
|
|
|
|
MKLINE(VolumeFader, curvol, sleUint8, VER_V8),
|
|
|
|
MKLINE(VolumeFader, speed_lo_max, sleUint16, VER_V8),
|
|
|
|
MKLINE(VolumeFader, num_steps, sleUint16, VER_V8),
|
|
|
|
MKLINE(VolumeFader, speed_hi, sleInt8, VER_V8),
|
|
|
|
MKLINE(VolumeFader, direction, sleInt8, VER_V8),
|
|
|
|
MKLINE(VolumeFader, speed_lo, sleInt8, VER_V8),
|
|
|
|
MKLINE(VolumeFader, speed_lo_counter, sleUint16, VER_V8),
|
2001-12-01 17:23:50 +00:00
|
|
|
MKEND()
|
|
|
|
};
|
|
|
|
|
|
|
|
const SaveLoadEntry partEntries[] = {
|
2002-12-08 16:14:29 +00:00
|
|
|
MKREF(Part, _next, TYPE_PART, VER_V8),
|
|
|
|
MKREF(Part, _prev, TYPE_PART, VER_V8),
|
|
|
|
MKREF(Part, _player, TYPE_PLAYER, VER_V8),
|
|
|
|
MKLINE(Part, _pitchbend, sleInt16, VER_V8),
|
|
|
|
MKLINE(Part, _pitchbend_factor, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _transpose, sleInt8, VER_V8),
|
|
|
|
MKLINE(Part, _vol, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _detune, sleInt8, VER_V8),
|
|
|
|
MKLINE(Part, _pan, sleInt8, VER_V8),
|
|
|
|
MKLINE(Part, _on, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _modwheel, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _pedal, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _program, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _pri, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _chan, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _effect_level, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _chorus, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _percussion, sleUint8, VER_V8),
|
|
|
|
MKLINE(Part, _bank, sleUint8, VER_V8),
|
2001-12-01 17:23:50 +00:00
|
|
|
MKEND()
|
|
|
|
};
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
#ifdef _WIN32_WCE // Don't break savegames made with andys' build
|
2002-04-11 17:19:16 +00:00
|
|
|
if (!ser->isSaving() && ser->checkEOFLoadStream())
|
|
|
|
return 0;
|
|
|
|
#endif
|
2002-03-14 14:45:04 +00:00
|
|
|
|
2001-12-01 17:23:50 +00:00
|
|
|
ser->_ref_me = this;
|
2002-12-08 16:14:29 +00:00
|
|
|
ser->_save_ref = saveReference;
|
|
|
|
ser->_load_ref = loadReference;
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
ser->saveLoadEntries(this, mainEntries);
|
2002-07-07 18:04:03 +00:00
|
|
|
ser->saveLoadArrayOf(_players, ARRAYSIZE(_players), sizeof(_players[0]), playerEntries);
|
|
|
|
ser->saveLoadArrayOf(_parts, ARRAYSIZE(_parts), sizeof(_parts[0]), partEntries);
|
2002-12-21 20:10:47 +00:00
|
|
|
|
2002-12-21 23:19:42 +00:00
|
|
|
{ // Load/save the instrument definitions, which were revamped with V11.
|
2002-12-21 20:10:47 +00:00
|
|
|
int i;
|
|
|
|
Part *part = &_parts[0];
|
2002-12-21 23:19:42 +00:00
|
|
|
if (ser->getVersion() >= VER_V11) {
|
|
|
|
for (i = ARRAYSIZE(_parts); i; --i, ++part) {
|
|
|
|
part->_program = 255;
|
|
|
|
part->_instrument.saveOrLoad (ser);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = ARRAYSIZE(_parts); i; --i, ++part)
|
|
|
|
part->_instrument.clear();
|
2002-12-21 20:10:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
ser->saveLoadArrayOf(_volume_fader, ARRAYSIZE(_volume_fader),
|
2002-12-15 01:55:27 +00:00
|
|
|
sizeof(_volume_fader[0]), volumeFaderEntries);
|
2001-12-01 17:23:50 +00:00
|
|
|
|
|
|
|
if (!ser->isSaving()) {
|
2002-12-15 01:55:27 +00:00
|
|
|
// Load all sounds that we need
|
2002-04-14 18:13:08 +00:00
|
|
|
fix_players_after_load(scumm);
|
2001-12-01 17:23:50 +00:00
|
|
|
init_sustaining_notes();
|
|
|
|
_active_volume_faders = true;
|
|
|
|
fix_parts_after_load();
|
|
|
|
_driver->update_pris();
|
2002-12-07 18:03:10 +00:00
|
|
|
set_master_volume (_master_volume);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef MKLINE
|
|
|
|
#undef MKEND
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::fix_parts_after_load() {
|
2001-12-01 17:23:50 +00:00
|
|
|
Part *part;
|
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (part->_player)
|
|
|
|
part->fix_after_load();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Only call this routine from the main thread,
|
|
|
|
// since it uses getResourceAddress
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseInternal::fix_players_after_load(Scumm *scumm) {
|
2001-12-01 17:23:50 +00:00
|
|
|
Player *player = _players;
|
|
|
|
int i;
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (player->_active) {
|
|
|
|
player->set_tempo(player->_tempo);
|
2002-04-14 18:13:08 +00:00
|
|
|
scumm->getResourceAddress(rtSound, player->_id);
|
2002-05-04 00:20:39 +00:00
|
|
|
player->_mt32emulate = isMT32(player->_id);
|
2002-09-29 11:11:42 +00:00
|
|
|
player->_isGM = isGM(player->_id);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_detune(int8 detune) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_detune_eff = clamp((_detune = detune) + _player->_detune, -128, 127);
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcMod);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_pitchbend(int value) {
|
2002-09-24 23:45:25 +00:00
|
|
|
_pitchbend = value;
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcMod);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_vol(uint8 vol) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_vol_eff = ((_vol = vol) + 1) * _player->_vol_eff >> 7;
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcVolume);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_pri(int8 pri) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_pri_eff = clamp((_pri = pri) + _player->_priority, 0, 255);
|
2002-11-21 19:06:42 +00:00
|
|
|
changed(IMuseDriver::pcPriority);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_pan(int8 pan) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_pan_eff = clamp((_pan = pan) + _player->_pan, -64, 63);
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcPan);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_transpose(int8 transpose) {
|
2002-07-07 18:04:03 +00:00
|
|
|
_transpose_eff = transpose_clamp((_transpose = transpose) + _player->_transpose, -12, 12);
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcMod);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_pedal(bool value) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_pedal = value;
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcPedal);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_modwheel(uint value) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_modwheel = value;
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcModwheel);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_chorus(uint chorus) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_chorus = chorus;
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcChorus);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2002-04-11 17:19:16 +00:00
|
|
|
void Part::set_effect_level(uint level)
|
|
|
|
{
|
2001-12-01 17:23:50 +00:00
|
|
|
_effect_level = level;
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcEffectLevel);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::fix_after_load() {
|
2001-12-01 17:23:50 +00:00
|
|
|
set_transpose(_transpose);
|
|
|
|
set_vol(_vol);
|
|
|
|
set_detune(_detune);
|
|
|
|
set_pri(_pri);
|
|
|
|
set_pan(_pan);
|
2002-12-18 14:57:25 +00:00
|
|
|
if (_program < 128) _instrument.program (_program, _player->_mt32emulate);
|
2002-12-07 17:10:18 +00:00
|
|
|
changed (IMuseDriver::pcAll);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_pitchbend_factor(uint8 value) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (value > 12)
|
|
|
|
return;
|
|
|
|
set_pitchbend(0);
|
|
|
|
_pitchbend_factor = value;
|
2002-10-13 06:07:39 +00:00
|
|
|
changed (IMuseDriver::pcPitchBendFactor);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_onoff(bool on) {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (_on != on) {
|
|
|
|
_on = on;
|
|
|
|
if (!on)
|
|
|
|
off();
|
|
|
|
if (!_percussion)
|
|
|
|
update_pris();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_instrument(byte * data) {
|
2002-12-18 13:22:40 +00:00
|
|
|
_instrument.adlib (data);
|
|
|
|
changed(IMuseDriver::pcProgram);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::load_global_instrument (byte slot) {
|
2002-12-20 13:09:01 +00:00
|
|
|
_drv->part_load_global_instrument (this, slot);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::key_on(byte note, byte velocity) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_drv->part_key_on(this, note, velocity);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::key_off(byte note) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_drv->part_key_off(this, note);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::init(IMuseDriver * driver) {
|
2002-04-13 18:34:11 +00:00
|
|
|
_drv = driver;
|
2001-12-01 17:23:50 +00:00
|
|
|
_player = NULL;
|
|
|
|
_next = NULL;
|
|
|
|
_prev = NULL;
|
|
|
|
_mc = NULL;
|
2002-11-26 16:54:58 +00:00
|
|
|
memset(_actives, 0, sizeof (_actives));
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::setup(Player *player) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_player = player;
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Insert first into player's list
|
2001-12-01 17:23:50 +00:00
|
|
|
_prev = NULL;
|
2002-04-11 17:19:16 +00:00
|
|
|
_next = player->_parts;
|
2001-12-01 17:23:50 +00:00
|
|
|
if (player->_parts)
|
|
|
|
player->_parts->_prev = this;
|
|
|
|
player->_parts = this;
|
|
|
|
|
2002-11-06 21:34:38 +00:00
|
|
|
_percussion = (player->_isGM && _chan == 9); // true;
|
2001-12-01 17:23:50 +00:00
|
|
|
_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;
|
2002-10-14 16:42:57 +00:00
|
|
|
_pitchbend_factor = 2;
|
2001-12-01 17:23:50 +00:00
|
|
|
_pitchbend = 0;
|
|
|
|
_effect_level = 64;
|
2002-12-18 17:14:05 +00:00
|
|
|
// _program = player->_se->get_channel_program (_chan);
|
|
|
|
// _instrument.program (_program, player->_mt32emulate);
|
|
|
|
_program = 255;
|
|
|
|
_instrument.clear();
|
2001-12-01 17:23:50 +00:00
|
|
|
_chorus = 0;
|
|
|
|
_modwheel = 0;
|
|
|
|
_bank = 0;
|
|
|
|
_pedal = false;
|
|
|
|
_mc = NULL;
|
2002-11-06 21:34:38 +00:00
|
|
|
|
|
|
|
if (_program < 128)
|
|
|
|
changed (IMuseDriver::pcAll);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::uninit() {
|
2001-12-01 17:23:50 +00:00
|
|
|
if (!_player)
|
|
|
|
return;
|
|
|
|
off();
|
2002-04-11 17:19:16 +00:00
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
// Unlink
|
2001-12-01 17:23:50 +00:00
|
|
|
if (_next)
|
|
|
|
_next->_prev = _prev;
|
|
|
|
if (_prev)
|
|
|
|
_prev->_next = _next;
|
|
|
|
else
|
|
|
|
_player->_parts = _next;
|
|
|
|
_player = NULL;
|
|
|
|
_next = NULL;
|
|
|
|
_prev = NULL;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::off() {
|
2001-12-01 17:23:50 +00:00
|
|
|
_drv->part_off(this);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::changed(uint16 what) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_drv->part_changed(this, what);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_param(byte param, int value) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_drv->part_set_param(this, param, value);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::update_pris() {
|
2001-12-01 17:23:50 +00:00
|
|
|
_drv->update_pris();
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int Part::update_actives(uint16 *active) {
|
2001-12-01 17:23:50 +00:00
|
|
|
return _drv->part_update_active(this, active);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_program(byte program) {
|
2002-04-11 17:19:16 +00:00
|
|
|
if (_program != program || _bank != 0) {
|
2001-12-01 17:23:50 +00:00
|
|
|
_program = program;
|
|
|
|
_bank = 0;
|
2002-12-18 13:22:40 +00:00
|
|
|
_instrument.program (_program, _player->_mt32emulate);
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcProgram);
|
2001-12-01 17:23:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void Part::set_instrument(uint b) {
|
2002-04-11 17:19:16 +00:00
|
|
|
_bank = (byte)(b >> 8);
|
2001-12-01 17:23:50 +00:00
|
|
|
_program = (byte)b;
|
2002-12-18 13:22:40 +00:00
|
|
|
_instrument.program (_program, _player->_mt32emulate);
|
2002-04-14 18:13:08 +00:00
|
|
|
changed(IMuseDriver::pcProgram);
|
|
|
|
}
|
|
|
|
|
2002-12-15 01:55:27 +00:00
|
|
|
////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// General MIDI implementation of iMuse
|
|
|
|
//
|
|
|
|
////////////////////////////////////////
|
2002-04-14 18:13:08 +00:00
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
IMuseDriver::IMuseDriver (MidiDriver *midi) {
|
2002-11-10 13:44:53 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
// Initialize our "last" trackers with impossible
|
|
|
|
// values, so that they don't accidentally match
|
|
|
|
// any changes that are sent (which would cause
|
|
|
|
// the changes to be ignored).
|
|
|
|
for (i = 0; i < 16; ++i) {
|
|
|
|
_midi_program_last [i] =
|
|
|
|
_midi_pitchbend_factor_last [i] =
|
|
|
|
_midi_volume_last [i] =
|
|
|
|
_midi_modwheel_last [i] =
|
|
|
|
_midi_effectlevel_last [i] =
|
|
|
|
_midi_chorus_last [i] = 255;
|
|
|
|
_midi_pan_last [i] = 127;
|
|
|
|
_midi_pitchbend_last [i] = (int16) -1;
|
|
|
|
_midi_pedal_last [i] = false;
|
|
|
|
}
|
|
|
|
_md = midi;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiPitchBend(byte chan, int16 pitchbend) {
|
2002-04-14 18:13:08 +00:00
|
|
|
uint16 tmp;
|
|
|
|
|
|
|
|
if (_midi_pitchbend_last[chan] != pitchbend) {
|
|
|
|
_midi_pitchbend_last[chan] = pitchbend;
|
2002-09-24 23:45:25 +00:00
|
|
|
tmp = pitchbend + 0x2000;
|
2002-04-14 18:13:08 +00:00
|
|
|
_md->send(((tmp >> 7) & 0x7F) << 16 | (tmp & 0x7F) << 8 | 0xE0 | chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-18 13:22:40 +00:00
|
|
|
void IMuseDriver::midiPitchBendFactor (byte chan, byte factor) {
|
2002-10-13 06:07:39 +00:00
|
|
|
if (_midi_pitchbend_factor_last[chan] != factor) {
|
|
|
|
_midi_pitchbend_factor_last[chan] = factor;
|
2002-11-04 18:29:26 +00:00
|
|
|
_md->setPitchBendRange (chan, factor);
|
2002-10-13 06:07:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiVolume(byte chan, byte volume) {
|
2002-04-14 18:13:08 +00:00
|
|
|
if (_midi_volume_last[chan] != volume) {
|
|
|
|
_midi_volume_last[chan] = volume;
|
|
|
|
_md->send(volume << 16 | 7 << 8 | 0xB0 | chan);
|
|
|
|
}
|
|
|
|
}
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiPedal(byte chan, bool pedal) {
|
2002-04-14 18:13:08 +00:00
|
|
|
if (_midi_pedal_last[chan] != pedal) {
|
|
|
|
_midi_pedal_last[chan] = pedal;
|
|
|
|
_md->send(pedal << 16 | 64 << 8 | 0xB0 | chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiModWheel(byte chan, byte modwheel) {
|
2002-04-14 18:13:08 +00:00
|
|
|
if (_midi_modwheel_last[chan] != modwheel) {
|
|
|
|
_midi_modwheel_last[chan] = modwheel;
|
|
|
|
_md->send(modwheel << 16 | 1 << 8 | 0xB0 | chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiEffectLevel(byte chan, byte level) {
|
2002-04-14 18:13:08 +00:00
|
|
|
if (_midi_effectlevel_last[chan] != level) {
|
|
|
|
_midi_effectlevel_last[chan] = level;
|
|
|
|
_md->send(level << 16 | 91 << 8 | 0xB0 | chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiChorus(byte chan, byte chorus) {
|
2002-04-14 18:13:08 +00:00
|
|
|
if (_midi_chorus_last[chan] != chorus) {
|
|
|
|
_midi_chorus_last[chan] = chorus;
|
|
|
|
_md->send(chorus << 16 | 93 << 8 | 0xB0 | chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiControl0(byte chan, byte value) {
|
2002-04-14 18:13:08 +00:00
|
|
|
_md->send(value << 16 | 0 << 8 | 0xB0 | chan);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiPan(byte chan, int8 pan) {
|
2002-04-14 18:13:08 +00:00
|
|
|
if (_midi_pan_last[chan] != pan) {
|
|
|
|
_midi_pan_last[chan] = pan;
|
|
|
|
_md->send(((pan - 64) & 0x7F) << 16 | 10 << 8 | 0xB0 | chan);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiNoteOn(byte chan, byte note, byte velocity) {
|
2002-04-14 18:13:08 +00:00
|
|
|
_md->send(velocity << 16 | note << 8 | 0x90 | chan);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiNoteOff(byte chan, byte note) {
|
2002-04-14 18:13:08 +00:00
|
|
|
_md->send(note << 8 | 0x80 | chan);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::midiSilence(byte chan) {
|
2002-04-14 18:13:08 +00:00
|
|
|
_md->send((64 << 8) | 0xB0 | chan);
|
|
|
|
_md->send((123 << 8) | 0xB0 | chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::part_key_on(Part *part, byte note, byte velocity) {
|
2002-12-01 20:16:54 +00:00
|
|
|
MidiChannel *mc = part->_mc;
|
2002-04-14 18:13:08 +00:00
|
|
|
|
2002-11-26 16:54:58 +00:00
|
|
|
part->_actives[note >> 4] |= (1 << (note & 0xF));
|
2002-04-14 18:13:08 +00:00
|
|
|
if (mc) {
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->noteOn (note, velocity);
|
2002-04-14 18:13:08 +00:00
|
|
|
} else if (part->_percussion) {
|
2002-11-26 16:54:58 +00:00
|
|
|
mc = _md->getPercussionChannel();
|
|
|
|
if (!mc)
|
|
|
|
return;
|
|
|
|
mc->volume (part->_vol_eff);
|
2002-12-18 13:22:40 +00:00
|
|
|
mc->programChange (part->_bank);
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->noteOn (note, velocity);
|
2002-04-14 18:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::part_key_off(Part *part, byte note) {
|
2002-12-01 20:16:54 +00:00
|
|
|
MidiChannel *mc = part->_mc;
|
2002-04-14 18:13:08 +00:00
|
|
|
|
2002-11-26 16:54:58 +00:00
|
|
|
part->_actives[note >> 4] &= ~(1 << (note & 0xF));
|
2002-04-14 18:13:08 +00:00
|
|
|
if (mc) {
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->noteOff (note);
|
2002-04-14 18:13:08 +00:00
|
|
|
} else if (part->_percussion) {
|
2002-11-26 16:54:58 +00:00
|
|
|
mc = _md->getPercussionChannel();
|
|
|
|
if (mc)
|
|
|
|
mc->noteOff (note);
|
2002-04-14 18:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::init(IMuseInternal *eng, OSystem *syst) {
|
2002-04-14 18:13:08 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
_system = syst;
|
|
|
|
|
2002-11-26 16:54:58 +00:00
|
|
|
// Open MIDI driver
|
2002-12-11 16:09:58 +00:00
|
|
|
int result = _md->open();
|
2002-04-14 18:13:08 +00:00
|
|
|
if (result)
|
2002-12-18 13:22:40 +00:00
|
|
|
error("IMuseDriver::error = %s", MidiDriver::getErrorName(result));
|
2002-04-14 18:13:08 +00:00
|
|
|
|
2002-11-26 16:54:58 +00:00
|
|
|
// Connect to the driver's timer
|
2002-04-14 18:13:08 +00:00
|
|
|
_se = eng;
|
2002-12-18 13:22:40 +00:00
|
|
|
_md->setTimerCallback (NULL, &IMuseDriver::timer_callback);
|
2002-04-14 18:13:08 +00:00
|
|
|
|
2002-11-26 16:54:58 +00:00
|
|
|
for (i = 0; i != ARRAYSIZE(_midi_program_last); i++) {
|
2002-11-06 21:34:38 +00:00
|
|
|
_midi_program_last [i] = 255;
|
|
|
|
}
|
2002-04-14 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
2002-12-18 13:22:40 +00:00
|
|
|
void IMuseDriver::timer_callback (void *) {
|
2002-12-03 17:54:25 +00:00
|
|
|
if (g_scumm->_imuse)
|
|
|
|
g_scumm->_imuse->on_timer();
|
2002-11-21 19:06:42 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::uninit() {
|
2002-08-22 12:09:06 +00:00
|
|
|
_md->close();
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::update_pris() {
|
2002-04-14 18:13:08 +00:00
|
|
|
Part *part, *hipart;
|
|
|
|
int i;
|
|
|
|
byte hipri, lopri;
|
2002-11-26 16:54:58 +00:00
|
|
|
Part *lopart;
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
while (true) {
|
|
|
|
hipri = 0;
|
|
|
|
hipart = NULL;
|
|
|
|
for (i = 32, part = _se->parts_ptr(); i; i--, part++) {
|
2002-07-07 18:04:03 +00:00
|
|
|
if (part->_player && !part->_percussion && part->_on && !part->_mc && part->_pri_eff >= hipri) {
|
2002-04-14 18:13:08 +00:00
|
|
|
hipri = part->_pri_eff;
|
|
|
|
hipart = part;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hipart)
|
|
|
|
return;
|
|
|
|
|
2002-12-18 17:14:05 +00:00
|
|
|
if ((hipart->_mc = _md->allocateChannel()) == NULL) {
|
|
|
|
lopri = 255;
|
|
|
|
lopart = NULL;
|
|
|
|
for (i = 32, part = _se->parts_ptr(); i; i--, part++) {
|
|
|
|
if (part->_mc && part->_pri_eff <= lopri) {
|
|
|
|
lopri = part->_pri_eff;
|
|
|
|
lopart = part;
|
|
|
|
}
|
2002-04-14 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
2002-12-18 17:14:05 +00:00
|
|
|
if (lopart == NULL || lopri >= hipri)
|
|
|
|
return;
|
|
|
|
lopart->off();
|
2002-11-26 16:54:58 +00:00
|
|
|
|
2002-12-18 17:14:05 +00:00
|
|
|
if ((hipart->_mc = _md->allocateChannel()) == NULL)
|
|
|
|
return;
|
|
|
|
}
|
2002-04-14 18:13:08 +00:00
|
|
|
hipart->changed(pcAll);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
int IMuseDriver::part_update_active(Part *part, uint16 *active) {
|
2002-04-14 18:13:08 +00:00
|
|
|
int i, j;
|
|
|
|
uint16 *act, mask, bits;
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
bits = 1 << part->_chan;
|
2002-11-26 16:54:58 +00:00
|
|
|
act = part->_actives;
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::set_instrument(uint slot, byte *data) {
|
2002-12-18 13:22:40 +00:00
|
|
|
if (slot < 32) {
|
|
|
|
// memcpy(&_glob_instr[slot], data, sizeof(Instrument));
|
|
|
|
_glob_instr[slot].adlib (data);
|
|
|
|
}
|
2002-11-21 19:06:42 +00:00
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::part_load_global_instrument (Part *part, byte slot) {
|
2002-12-20 13:09:01 +00:00
|
|
|
if (slot >= 32)
|
|
|
|
return;
|
|
|
|
_glob_instr [slot].copy_to (&part->_instrument);
|
|
|
|
part->changed (pcProgram);
|
|
|
|
}
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::part_changed(Part *part, uint16 what) {
|
2002-12-01 20:16:54 +00:00
|
|
|
MidiChannel *mc;
|
2002-04-14 18:13:08 +00:00
|
|
|
|
2002-11-26 16:54:58 +00:00
|
|
|
// Mark for re-schedule if program changed when in pre-state
|
2002-12-18 17:14:05 +00:00
|
|
|
if (what & pcProgram && !part->_mc && part->_on && !part->_percussion)
|
2002-04-14 18:13:08 +00:00
|
|
|
update_pris();
|
|
|
|
|
2002-12-01 20:16:54 +00:00
|
|
|
if (!(mc = part->_mc))
|
2002-04-14 18:13:08 +00:00
|
|
|
return;
|
|
|
|
|
2002-11-26 16:54:58 +00:00
|
|
|
if (part->_player == NULL) { // No player, so dump phantom channel
|
|
|
|
part->_mc->release();
|
2002-05-09 18:03:07 +00:00
|
|
|
part->_mc = NULL;
|
2002-11-26 16:54:58 +00:00
|
|
|
memset(part->_actives, 0, sizeof(part->_actives));
|
2002-05-09 18:03:07 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2002-10-13 06:07:39 +00:00
|
|
|
if (what & pcPitchBendFactor)
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->pitchBendFactor (part->_pitchbend_factor);
|
2002-10-13 06:07:39 +00:00
|
|
|
|
2002-04-14 18:13:08 +00:00
|
|
|
if (what & pcMod)
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->pitchBend (clamp(part->_pitchbend +
|
|
|
|
(part->_detune_eff * 64 / 12) +
|
|
|
|
(part->_transpose_eff * 8192 / 12), -8192, 8191));
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
if (what & pcVolume)
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->volume (part->_vol_eff);
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
if (what & pcPedal)
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->sustain (part->_pedal);
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
if (what & pcModwheel)
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->modulationWheel (part->_modwheel);
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
if (what & pcPan)
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->panPosition (part->_pan_eff);
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
if (what & pcEffectLevel)
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->effectLevel (part->_effect_level);
|
2002-04-14 18:13:08 +00:00
|
|
|
|
2002-12-18 13:22:40 +00:00
|
|
|
if (what & pcProgram)
|
|
|
|
part->_instrument.send (mc);
|
2002-04-14 18:13:08 +00:00
|
|
|
|
|
|
|
if (what & pcChorus)
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->chorusLevel (part->_effect_level);
|
2002-11-21 19:06:42 +00:00
|
|
|
|
|
|
|
if (what & pcPriority)
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->priority (part->_pri_eff);
|
2002-04-14 18:13:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-03-06 17:58:13 +00:00
|
|
|
void IMuseDriver::part_off(Part *part) {
|
2002-12-01 20:16:54 +00:00
|
|
|
MidiChannel *mc = part->_mc;
|
2002-04-14 18:13:08 +00:00
|
|
|
if (mc) {
|
2002-11-26 16:54:58 +00:00
|
|
|
mc->allNotesOff();
|
|
|
|
mc->release();
|
2002-04-14 18:13:08 +00:00
|
|
|
part->_mc = NULL;
|
2002-11-26 16:54:58 +00:00
|
|
|
memset(part->_actives, 0, sizeof(part->_actives));
|
2002-04-14 18:13:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-21 21:09:36 +00:00
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// IMuse implementation
|
|
|
|
//
|
|
|
|
// IMuse actually serves as a concurency monitor front-end
|
|
|
|
// to IMuseInternal and ensures that only one thread
|
|
|
|
// accesses the object at a time. This is necessary to
|
|
|
|
// prevent scripts and the MIDI parser from yanking objects
|
|
|
|
// out from underneath each other.
|
|
|
|
//
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
IMuse::IMuse (OSystem *system, IMuseInternal *target) : _system (system), _target (target) { _mutex = system->create_mutex(); }
|
|
|
|
IMuse::~IMuse() { if (_mutex) _system->delete_mutex (_mutex); if (_target) delete _target; }
|
|
|
|
inline void IMuse::in() { _system->lock_mutex (_mutex); }
|
|
|
|
inline void IMuse::out() { _system->unlock_mutex (_mutex); }
|
|
|
|
|
|
|
|
void IMuse::on_timer() { in(); _target->on_timer(); out(); }
|
|
|
|
void IMuse::pause(bool paused) { in(); _target->pause (paused); out(); }
|
|
|
|
int IMuse::save_or_load(Serializer *ser, Scumm *scumm) { in(); int ret = _target->save_or_load (ser, scumm); out(); return ret; }
|
|
|
|
int IMuse::set_music_volume(uint vol) { in(); int ret = _target->set_music_volume (vol); out(); return ret; }
|
|
|
|
int IMuse::get_music_volume() { in(); int ret = _target->get_music_volume(); out(); return ret; }
|
|
|
|
int IMuse::set_master_volume(uint vol) { in(); int ret = _target->set_master_volume (vol); out(); return ret; }
|
|
|
|
int IMuse::get_master_volume() { in(); int ret = _target->get_master_volume(); out(); return ret; }
|
2003-03-18 16:13:52 +00:00
|
|
|
bool IMuse::startSound(int sound) { in(); bool ret = _target->startSound (sound); out(); return ret; }
|
|
|
|
int IMuse::stopSound(int sound) { in(); int ret = _target->stopSound (sound); out(); return ret; }
|
2002-12-21 21:09:36 +00:00
|
|
|
int IMuse::stop_all_sounds() { in(); int ret = _target->stop_all_sounds(); out(); return ret; }
|
2003-03-23 13:14:54 +00:00
|
|
|
int IMuse::getSoundStatus(int sound) { in(); int ret = _target->getSoundStatus (sound); out(); return ret; }
|
2002-12-21 21:09:36 +00:00
|
|
|
bool IMuse::get_sound_active(int sound) { in(); bool ret = _target->get_sound_active (sound); out(); return ret; }
|
2003-03-18 16:13:52 +00:00
|
|
|
int32 IMuse::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) { in(); int32 ret = _target->doCommand (a,b,c,d,e,f,g,h); out(); return ret; }
|
2002-12-21 21:09:36 +00:00
|
|
|
int IMuse::clear_queue() { in(); int ret = _target->clear_queue(); out(); return ret; }
|
|
|
|
void IMuse::setBase(byte **base) { in(); _target->setBase (base); out(); }
|
|
|
|
uint32 IMuse::property(int prop, uint32 value) { in(); uint32 ret = _target->property (prop, value); out(); return ret; }
|
|
|
|
|
2002-10-21 07:31:51 +00:00
|
|
|
// The IMuse::create method provides a front-end factory
|
|
|
|
// for creating IMuseInternal without exposing that class
|
|
|
|
// to the client.
|
2003-03-06 17:58:13 +00:00
|
|
|
IMuse *IMuse::create (OSystem *syst, MidiDriver *midi, SoundMixer *mixer) {
|
2002-12-21 21:09:36 +00:00
|
|
|
IMuseInternal *engine = IMuseInternal::create (syst, midi, mixer);
|
2002-12-01 15:51:29 +00:00
|
|
|
if (midi)
|
|
|
|
midi->property (MidiDriver::PROP_SMALLHEADER, (g_scumm->_features & GF_SMALL_HEADER) ? 1 : 0);
|
2002-12-21 21:09:36 +00:00
|
|
|
return new IMuse (syst, engine);
|
2002-04-29 11:48:33 +00:00
|
|
|
}
|