scummvm/engines/scumm/imuse/imuse_internal.h

539 lines
14 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*/
#ifndef SCUMM_IMUSE_INTERNAL
#define SCUMM_IMUSE_INTERNAL
#include "common/scummsys.h"
#include "scumm/imuse/imuse.h"
#include "scumm/imuse/instrument.h"
#include "scumm/saveload.h"
#include "sound/mididrv.h"
class MidiParser;
class OSystem;
namespace Scumm {
struct ParameterFader;
struct DeferredCommand;
struct ImTrigger;
struct SustainingNotes;
struct CommandQueue;
struct IsNoteCmdData;
class Player;
struct Part;
class IMuseInternal;
class IMuseSysex_Scumm;
//////////////////////////////////////////////////
//
// Some constants
//
//////////////////////////////////////////////////
#define TICKS_PER_BEAT 480
#define TRIGGER_ID 0
#define COMMAND_ID 1
#define MDPG_TAG "MDpg"
////////////////////////////////////////
//
// Helper functions
//
////////////////////////////////////////
inline int clamp(int val, int min, int max) {
if (val < min)
return min;
if (val > max)
return max;
return val;
}
inline int transpose_clamp(int a, int b, int c) {
if (b > a)
a += (b - a + 11) / 12 * 12;
if (c < a)
a -= (a - c + 11) / 12 * 12;
return a;
}
//////////////////////////////////////////////////
//
// Entity declarations
//
//////////////////////////////////////////////////
struct TimerCallbackInfo {
IMuseInternal *imuse;
MidiDriver *driver;
};
struct HookDatas {
byte _jump[2];
byte _transpose;
byte _part_onoff[16];
byte _part_volume[16];
byte _part_program[16];
byte _part_transpose[16];
int query_param(int param, byte chan);
int set(byte cls, byte value, byte chan);
HookDatas() { memset(this, 0, sizeof(HookDatas)); }
};
struct ParameterFader {
enum {
pfVolume = 1,
pfTranspose = 3,
pfSpeed = 4
};
int param;
int start;
int end;
uint32 total_time;
uint32 current_time;
ParameterFader() { param = 0; }
void init() { param = 0; }
};
struct DeferredCommand {
uint32 time_left;
int a, b, c, d, e, f;
DeferredCommand() { memset(this, 0, sizeof(DeferredCommand)); }
};
struct ImTrigger {
int sound;
byte id;
uint16 expire;
int command [8];
ImTrigger() { memset(this, 0, sizeof(ImTrigger)); }
};
struct CommandQueue {
uint16 array[8];
CommandQueue() { memset(this, 0, sizeof(CommandQueue)); }
};
//////////////////////////////////////////////////
//
// Player class definition
//
//////////////////////////////////////////////////
class Player : public MidiDriver {
/*
* External SysEx handler functions shall each be defined in
* a separate file. This header file shall be included at the
* top of the file immediately following this special #define:
* #define SYSEX_CALLBACK_FUNCTION nameOfHandlerFunction
*/
#ifdef SYSEX_CALLBACK_FUNCTION
friend void SYSEX_CALLBACK_FUNCTION(Player *, const byte *, uint16);
#endif
protected:
// Moved from IMuseInternal.
// This is only used by one player at a time.
static uint16 _active_notes[128];
protected:
MidiDriver *_midi;
MidiParser *_parser;
bool _passThrough; // Only respond to EOT, all else direct to MidiDriver
Part *_parts;
bool _active;
bool _scanning;
int _id;
byte _priority;
byte _volume;
int8 _pan;
int8 _transpose;
int8 _detune;
byte _vol_eff;
uint _track_index;
uint _loop_to_beat;
uint _loop_from_beat;
uint _loop_counter;
uint _loop_to_tick;
uint _loop_from_tick;
byte _speed;
bool _abort;
// This does not get used by us! It is only
// here for save/load purposes, and gets
// passed on to the MidiParser during
// fixAfterLoad().
uint32 _music_tick;
HookDatas _hook;
ParameterFader _parameterFaders[4];
bool _isMT32;
bool _isMIDI;
protected:
// Player part
void hook_clear();
void uninit_parts();
byte *parse_midi(byte *s);
void part_set_transpose(uint8 chan, byte relative, int8 b);
void parse_sysex(byte *p, uint len);
void maybe_jump(byte cmd, uint track, uint beat, uint tick);
void maybe_set_transpose(byte *data);
void maybe_part_onoff(byte *data);
void maybe_set_volume(byte *data);
void maybe_set_program(byte *data);
void maybe_set_transpose_part(byte *data);
void turn_off_pedals();
int query_part_param(int param, byte chan);
void turn_off_parts();
void play_active_notes();
void transitionParameters();
static void decode_sysex_bytes(const byte *src, byte *dst, int len);
// Sequencer part
int start_seq_sound(int sound, bool reset_vars = true);
int query_param(int param);
public:
IMuseInternal *_se;
uint _vol_chan;
public:
Player();
virtual ~Player();
int addParameterFader(int param, int target, int time);
void clear();
void clearLoop();
void fixAfterLoad();
Part * getActivePart(uint8 part);
uint getBeatIndex();
int8 getDetune() const { return _detune; }
byte getEffectiveVolume() const { return _vol_eff; }
int getID() const { return _id; }
MidiDriver *getMidiDriver() const { return _midi; }
int getParam(int param, byte chan);
int8 getPan() const { return _pan; }
Part * getPart(uint8 part);
byte getPriority() const { return _priority; }
uint getTicksPerBeat() const { return TICKS_PER_BEAT; }
int8 getTranspose() const { return _transpose; }
byte getVolume() const { return _volume; }
bool isActive() const { return _active; }
bool isFadingOut() const;
bool isMIDI() const { return _isMIDI; }
bool isMT32() const { return _isMT32; }
bool jump(uint track, uint beat, uint tick);
void onTimer();
void removePart(Part *part);
int scan(uint totrack, uint tobeat, uint totick);
void saveLoadWithSerializer(Serializer *ser);
int setHook(byte cls, byte value, byte chan) { return _hook.set(cls, value, chan); }
void setDetune(int detune);
bool setLoop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick);
void setPan(int pan);
void setPriority(int pri);
void setSpeed(byte speed);
int setTranspose(byte relative, int b);
int setVolume(byte vol);
bool startSound(int sound, MidiDriver *midi, bool passThrough);
int getMusicTimer() const;
public:
// MidiDriver interface
int open() { return 0; }
void close() { }
void send(uint32 b);
const char *getErrorName(int error_code) { return "Unknown"; }
void sysEx(const byte *msg, uint16 length);
void metaEvent(byte type, byte *data, uint16 length);
void setTimerCallback(void *timer_param, void(*timer_proc)(void *)) { }
uint32 getBaseTempo();
MidiChannel *allocateChannel() { return 0; }
MidiChannel *getPercussionChannel() { return 0; }
};
//////////////////////////////////////////////////
//
// Part pseudo-class definition
//
//////////////////////////////////////////////////
struct Part : public Serializable {
IMuseInternal *_se;
int _slot;
Part *_next, *_prev;
MidiChannel *_mc;
Player *_player;
int16 _pitchbend;
byte _pitchbend_factor;
int8 _transpose, _transpose_eff;
byte _vol, _vol_eff;
int8 _detune, _detune_eff;
int8 _pan, _pan_eff;
bool _on;
byte _modwheel;
bool _pedal;
int8 _pri;
byte _pri_eff;
byte _chan;
byte _effect_level;
byte _chorus;
byte _percussion;
byte _bank;
// New abstract instrument definition
Instrument _instrument;
bool _unassigned_instrument; // For diagnostic reporting purposes only
// MidiChannel interface
// (We don't currently derive from MidiChannel,
// but if we ever do, this will make it easy.)
void noteOff(byte note);
void noteOn(byte note, byte velocity);
void programChange(byte value);
void pitchBend(int16 value);
void modulationWheel(byte value);
void volume(byte value);
void pitchBendFactor(byte value);
void sustain(bool value);
void effectLevel(byte value);
void chorusLevel(byte value);
void allNotesOff();
void set_param(byte param, int value) { }
void init();
void setup(Player *player);
void uninit();
void off();
void set_instrument(uint b);
void set_instrument(byte *data);
void load_global_instrument(byte b);
void set_transpose(int8 transpose);
void set_detune(int8 detune);
void set_pri(int8 pri);
void set_pan(int8 pan);
void set_onoff(bool on);
void fix_after_load();
void sendAll();
void sendPitchBend();
bool clearToTransmit();
Part();
void saveLoadWithSerializer(Serializer *ser);
};
/**
* SCUMM implementation of IMuse.
* This class implements the IMuse mixin interface for the SCUMM environment.
*/
class IMuseInternal : public IMuse {
friend class Player;
friend struct Part;
/*
* External SysEx handler functions shall each be defined in
* a separate file. This header file shall be included at the
* top of the file immediately following this special #define:
* #define SYSEX_CALLBACK_FUNCTION nameOfHandlerFunction
*/
#ifdef SYSEX_CALLBACK_FUNCTION
friend void SYSEX_CALLBACK_FUNCTION(Player *, const byte *, uint16);
#endif
protected:
bool _native_mt32;
bool _enable_gs;
bool _sc55;
MidiDriver *_midi_adlib;
MidiDriver *_midi_native;
TimerCallbackInfo _timer_info_adlib;
TimerCallbackInfo _timer_info_native;
uint32 _game_id;
byte **_base_sounds;
// Plug-in SysEx handling. Right now this only supports one
// custom SysEx handler for the hardcoded IMUSE_SYSEX_ID
// manufacturer code. TODO: Expand this to support multiple
// SysEx handlers for client-specified manufacturer codes.
sysexfunc _sysex;
OSystem *_system;
Common::Mutex _mutex;
protected:
bool _paused;
bool _initialized;
int _tempoFactor;
int _player_limit; // Limits how many simultaneous music tracks are played
bool _recycle_players; // Can we stop a player in order to start another one?
bool _direct_passthrough; // Pass data direct to MidiDriver (no interactivity)
uint _queue_end, _queue_pos, _queue_sound;
byte _queue_adding;
byte _queue_marker;
byte _queue_cleared;
byte _master_volume; // Master volume. 0-255
byte _music_volume; // Global music volume. 0-255
uint16 _trigger_count;
ImTrigger _snm_triggers[16]; // Sam & Max triggers
uint16 _snm_trigger_index;
uint16 _channel_volume[8];
uint16 _channel_volume_eff[8]; // No Save
uint16 _volchan_table[8];
Player _players[8];
Part _parts[32];
Instrument _global_adlib_instruments[32];
CommandQueue _cmd_queue[64];
DeferredCommand _deferredCommands[4];
protected:
IMuseInternal();
int initialize(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
static void midiTimerCallback(void *data);
void on_timer(MidiDriver *midi);
byte *findStartOfSound(int sound);
bool isMT32(int sound);
bool isMIDI(int sound);
int get_queue_sound_status(int sound) const;
void handle_marker(uint id, byte data);
int get_channel_volume(uint a);
void initMidiDriver(TimerCallbackInfo *info);
void initGM(MidiDriver *midi);
void initMT32(MidiDriver *midi);
void init_players();
void init_parts();
void init_queue();
void sequencer_timers(MidiDriver *midi);
MidiDriver *getBestMidiDriver(int sound);
Player *allocate_player(byte priority);
Part *allocate_part(byte pri, MidiDriver *midi);
int32 ImSetTrigger(int sound, int id, int a, int b, int c, int d, int e, int f, int g, int h);
int32 ImClearTrigger(int sound, int id);
int32 ImFireAllTriggers(int sound);
void addDeferredCommand(int time, int a, int b, int c, int d, int e, int f);
void handleDeferredCommands(MidiDriver *midi);
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 clear_queue();
int query_queue(int param);
Player *findActivePlayer(int id);
int get_volchan_entry(uint a);
int set_volchan_entry(uint a, uint b);
int set_channel_volume(uint chan, uint vol);
void update_volumes();
void reset_tick();
int set_volchan(int sound, int volchan);
void fix_parts_after_load();
void fix_players_after_load(ScummEngine *scumm);
int setImuseMasterVolume(uint vol);
void reallocateMidiChannels(MidiDriver *midi);
void setGlobalAdlibInstrument(byte slot, byte *data);
void copyGlobalAdlibInstrument(byte slot, Instrument *dest);
bool isNativeMT32() { return _native_mt32; }
protected:
// Internal mutex-free versions of the IMuse and MusicEngine methods.
bool startSound_internal(int sound);
int stopSound_internal(int sound);
int stopAllSounds_internal();
int getSoundStatus_internal(int sound, bool ignoreFadeouts) const;
int32 doCommand_internal(int a, int b, int c, int d, int e, int f, int g, int h);
int32 doCommand_internal(int numargs, int args[]);
public:
// IMuse interface
void pause(bool paused);
int save_or_load(Serializer *ser, ScummEngine *scumm);
bool get_sound_active(int sound) const;
int32 doCommand(int numargs, int args[]);
void setBase(byte **base);
uint32 property(int prop, uint32 value);
virtual void addSysexHandler(byte mfgID, sysexfunc handler);
public:
// MusicEngine interface
void setMusicVolume(int vol);
void startSound(int sound);
void stopSound(int sound);
void stopAllSounds();
int getSoundStatus(int sound) const;
int getMusicTimer() const;
void terminate();
public:
// Factory function
static IMuseInternal *create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
};
} // End of namespace Scumm
#endif