mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-16 22:58:09 +00:00
218e132e37
svn-id: r27024
539 lines
14 KiB
C++
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
|