diff options
Diffstat (limited to 'scumm')
-rw-r--r-- | scumm/imuse.cpp | 1750 | ||||
-rw-r--r-- | scumm/imuse_internal.h | 460 | ||||
-rw-r--r-- | scumm/imuse_player.cpp | 1377 |
3 files changed, 1842 insertions, 1745 deletions
diff --git a/scumm/imuse.cpp b/scumm/imuse.cpp index 7166a36d3f..2179ed6318 100644 --- a/scumm/imuse.cpp +++ b/scumm/imuse.cpp @@ -26,499 +26,13 @@ #include "scumm/instrument.h" #include "scumm/saveload.h" #include "common/util.h" +#include "imuse_internal.h" // Unremark this statement to activate some of // the most common iMuse diagnostic messages. // #define IMUSE_DEBUG -// -// Some constants -// -#define TICKS_PER_BEAT 480 - -#define IMUSE_SYSEX_ID 0x7D -#define ROLAND_SYSEX_ID 0x41 -#define PERCUSSION_CHANNEL 9 - -#define TRIGGER_ID 0 -#define COMMAND_ID 1 - -#define MDPG_TAG "MDpg" -#define MDHD_TAG "MDhd" - -// 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. - -struct Part; -struct Player; - -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); -}; - -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 { - MidiDriver *midi; - uint32 time_left; - int a, b, c, d, e, f; -}; - -struct Player { - IMuseInternal *_se; - MidiDriver *_midi; - - Part *_parts; - bool _active; - bool _scanning; - int _id; - byte _priority; - byte _volume; - int8 _pan; - int8 _transpose; - int8 _detune; - uint _vol_chan; - byte _vol_eff; - - uint _song_index; - uint _track_index; - uint _timer_counter; - uint _loop_to_beat; - uint _loop_from_beat; - uint _loop_counter; - uint _loop_to_tick; - uint _loop_from_tick; - uint32 _tempo; - uint32 _tempo_eff; // No Save - uint32 _cur_pos; - uint32 _next_pos; - uint32 _song_offset; - uint32 _timer_speed; // No Save - uint _tick_index; - uint _beat_index; - uint _ticks_per_beat; - byte _speed; // No Save - bool _abort; - - HookDatas _hook; - ParameterFader _parameterFaders[4]; - - bool _mt32emulate; - bool _isGM; - - // Player part - void hook_clear(); - void clear(); - bool startSound (int sound, MidiDriver *midi); - void uninit_parts(); - byte *parse_midi(byte *s); - void key_off(uint8 chan, byte data); - void key_on(uint8 chan, byte data, byte velocity); - void part_set_transpose(uint8 chan, byte relative, int8 b); - void parse_sysex(byte *p, uint len); - void maybe_jump (byte cmd, uint track, uint beat, uint tick); - void maybe_set_transpose(byte *data); - void maybe_part_onoff(byte *data); - void maybe_set_volume(byte *data); - void maybe_set_program(byte *data); - void maybe_set_transpose_part(byte *data); - uint update_actives(); - Part *get_part(uint8 part); - void turn_off_pedals(); - int set_vol(byte vol); - int get_param(int param, byte chan); - int query_part_param(int param, byte chan); - int set_transpose(byte relative, int b); - void set_priority(int pri); - void set_pan(int pan); - void set_detune(int detune); - void turn_off_parts(); - void play_active_notes(); - void cancel_volume_fade(); - - int addParameterFader (int param, int target, int time); - void transitionParameters(); - - static void decode_sysex_bytes(byte *src, byte *dst, int len); - - void clear_active_note(int chan, byte note); - void set_active_note(int chan, byte note); - void clear_active_notes(); - - // Sequencer part - bool set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick); - void clear_loop(); - void set_speed(byte speed); - bool jump(uint track, uint beat, uint tick); - void uninit_seq(); - void set_tempo(uint32 data); - int start_seq_sound(int sound); - void find_sustaining_notes(byte *a, byte *b, uint32 l); - int scan(uint totrack, uint tobeat, uint totick); - int query_param(int param); - - bool is_fading_out(); - void sequencer_timer(); - - Player() { - memset(this,0,sizeof(Player)); - } - -}; - -struct SustainingNotes { - SustainingNotes *next; - SustainingNotes *prev; - Player *player; - byte note, chan; - uint32 off_pos; - uint32 pos; - uint16 counter; -}; - -struct CommandQueue { - uint16 array[8]; -}; - -struct IsNoteCmdData { - byte chan; - byte note; - byte vel; -}; - -struct Part { - 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 - - // Used to be in MidiDriver - uint16 _actives[8]; - - void key_on(byte note, byte velocity); - void key_off(byte note); - void set_param(byte param, int value) { } - void init(); - void setup(Player *player); - void uninit(); - void off(); - void silence(); - void set_instrument(uint b); - void set_instrument(byte *data); - void load_global_instrument (byte b); - - void set_transpose(int8 transpose); - void set_vol(uint8 volume); - void set_detune(int8 detune); - void set_pri(int8 pri); - void set_pan(int8 pan); - void set_modwheel(uint value); - void set_pedal(bool value); - void set_pitchbend(int value); - void release_pedal(); - void set_program(byte program); - void set_chorus(uint chorus); - void set_effect_level(uint level); - - int update_actives(uint16 *active); - void set_pitchbend_factor(uint8 value); - void set_onoff(bool on); - void fix_after_load(); - - void sendAll(); - bool clearToTransmit(); - - Part() { - memset(this,0,sizeof(Part)); - } -}; - -struct ImTrigger { - int sound; - byte id; - uint16 expire; - byte command [4]; -}; - -// WARNING: This is the internal variant of the IMUSE class. -// imuse.h contains a public version of the same class. -// the public version, only contains a set of methods. -class IMuseInternal { - friend struct Player; - -private: - bool _old_adlib_instruments; - bool _enable_multi_midi; - MidiDriver *_midi_adlib; - MidiDriver *_midi_native; - - byte **_base_sounds; - -private: - bool _paused; - bool _initialized; - - int _tempoFactor; - - uint _queue_end, _queue_pos, _queue_sound; - byte _queue_adding; - - SustainingNotes *_sustain_notes_used; - SustainingNotes *_sustain_notes_free; - SustainingNotes *_sustain_notes_head; - - byte _queue_marker; - byte _queue_cleared; - byte _master_volume; // Master volume. 0-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]; - SustainingNotes _sustaining_notes[24]; - Part _parts[32]; - - uint16 _active_notes[128]; - Instrument _global_adlib_instruments[32]; - CommandQueue _cmd_queue[64]; - DeferredCommand _deferredCommands[4]; - - byte *findTag(int sound, char *tag, int index); - bool isMT32(int sound); - bool isGM(int sound); - int get_queue_sound_status(int sound); - void handle_marker(uint id, byte data); - int get_channel_volume(uint a); - void initMidiDriver (MidiDriver *midi); - void init_players(); - void init_parts(); - void init_sustaining_notes(); - void init_queue(); - - void sequencer_timers (MidiDriver *midi); - void expire_sustain_notes (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); - 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 query_queue(int param); - Player *get_player_byid(int id); - - int get_volchan_entry(uint a); - int set_volchan_entry(uint a, uint b); - int set_channel_volume(uint chan, uint vol); - void update_volumes(); - void reset_tick(); - - int set_volchan(int sound, int volchan); - - void fix_parts_after_load(); - void fix_players_after_load(Scumm *scumm); - - static int saveReference(void *me_ref, byte type, void *ref); - static void *loadReference(void *me_ref, byte type, int ref); - - static void midiTimerCallback (void *data); - -public: - IMuseInternal() { - memset(this,0,sizeof(IMuseInternal)); - } - ~IMuseInternal(); - - int initialize(OSystem *syst, MidiDriver *midi); - void reallocateMidiChannels (MidiDriver *midi); - void setGlobalAdlibInstrument (byte slot, byte *data); - void copyGlobalAdlibInstrument (byte slot, Instrument *dest); - - // IMuse interface - - void on_timer (MidiDriver *midi); - void pause(bool paused); - int terminate(); - int save_or_load(Serializer *ser, Scumm *scumm); - int set_music_volume(uint vol); - int get_music_volume(); - int set_master_volume(uint vol); - int get_master_volume(); - bool startSound(int sound); - int stopSound(int sound); - int stop_all_sounds(); - int getSoundStatus(int sound); - bool get_sound_active(int sound); - int32 doCommand(int a, int b, int c, int d, int e, int f, int g, int h); - int clear_queue(); - void setBase(byte **base); - - uint32 property(int prop, uint32 value); - MidiDriver *getMidiDriver(); - - static IMuseInternal *create(OSystem *syst, MidiDriver *midi); -}; - -//////////////////////////////////////// -// -// IMUSE helper functions -// -//////////////////////////////////////// - -static int clamp(int val, int min, int max) { - if (val < min) - return min; - if (val > max) - return max; - return val; -} - -static int transpose_clamp(int a, int b, int c) { - if (b > a) - a += (b - a + 11) / 12 * 12; - if (c < a) - a -= (a - c + 11) / 12 * 12; - return a; -} - -static uint32 get_delta_time(byte **s) { - byte *d = *s, b; - uint32 time = 0; - do { - b = *d++; - time = (time << 7) | (b & 0x7F); - } while (b & 0x80); - *s = d; - return time; -} - -static uint read_word(byte *a) { - return (a[0] << 8) + a[1]; -} - -static void skip_midi_cmd(byte **song_ptr) { - byte *s, code; - const byte num_skip[] = { - 2, 2, 2, 2, 1, 1, 2 - }; - - s = *song_ptr; - - code = *s++; - - if (code < 0x80) { - s = NULL; - } else if (code < 0xF0) { - s += num_skip[(code & 0x70) >> 4]; - } else { - if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) { - s += get_delta_time(&s); - } else { - s = NULL; - } - } - *song_ptr = s; -} - -static int is_note_cmd(byte **a, IsNoteCmdData * isnote) { - byte *s = *a; - byte code; - - code = *s++; - - switch (code >> 4) { - case 8: // Key Off - isnote->chan = code & 0xF; - isnote->note = *s++; - isnote->vel = *s++; - *a = s; - return 1; - case 9: // Key On - isnote->chan = code & 0xF; - isnote->note = *s++; - isnote->vel = *s++; - *a = s; - if (isnote->vel) - return 2; - return 1; - case 0xA: - case 0xB: - case 0xE: - s++; - case 0xC: - case 0xD: - s++; - break; - case 0xF: - if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) { - s += get_delta_time(&s); - break; - } - return -1; - default: - return -1; - } - *a = s; - return 0; -} //////////////////////////////////////// // @@ -1646,1159 +1160,6 @@ void IMuseInternal::pause(bool paused) { _paused = paused; } -//////////////////////////////////////// -// -// Player implementation -// -//////////////////////////////////////// - -bool Player::is_fading_out() { - int i; - for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) { - if (_parameterFaders[i].param == ParameterFader::pfVolume && - _parameterFaders[i].end == 0) - { - return true; - } - } - return false; -} - -void Player::clear() { - uninit_seq(); - cancel_volume_fade(); - uninit_parts(); - _se->ImFireAllTriggers (_id); - _active = false; - _ticks_per_beat = TICKS_PER_BEAT; - _midi = NULL; -} - -bool Player::startSound (int sound, MidiDriver *midi) { - void *mdhd; - int i; - - mdhd = _se->findTag(sound, MDHD_TAG, 0); - if (mdhd == NULL) { - mdhd = _se->findTag(sound, MDPG_TAG, 0); - if (mdhd == NULL) { - warning("P::startSound failed: Couldn't find %s", MDHD_TAG); - return false; - } - } - - _mt32emulate = _se->isMT32(sound); - _isGM = _se->isGM(sound); - - _parts = NULL; - _active = true; - _midi = midi; - _id = sound; - _priority = 0x80; - _volume = 0x7F; - _vol_chan = 0xFFFF; - _vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7; - _pan = 0; - _transpose = 0; - _detune = 0; - - for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) - _parameterFaders[i].init(); - - hook_clear(); - if (start_seq_sound(sound) != 0) { - _active = false; - _midi = NULL; - return false; - } - return true; -} - -void Player::hook_clear() { - memset(&_hook, 0, sizeof(_hook)); -} - -int Player::start_seq_sound(int sound) { - byte *ptr, *track_ptr; - - _song_index = sound; - _timer_counter = 0; - _loop_to_beat = 1; - _loop_from_beat = 1; - _track_index = 0; - _loop_counter = 0; - _loop_to_tick = 0; - _loop_from_tick = 0; - - set_tempo(500000); - set_speed(128); - ptr = _se->findTag(sound, "MTrk", _track_index); - if (ptr == NULL) - return -1; - - track_ptr = ptr; - _cur_pos = _next_pos = get_delta_time(&track_ptr); - _song_offset = track_ptr - ptr; - - _tick_index = _cur_pos; - _beat_index = 1; - - if (_tick_index >= _ticks_per_beat) { - _beat_index += _tick_index / _ticks_per_beat; - _tick_index %= _ticks_per_beat; - } - - return 0; -} - -void Player::set_tempo(uint32 b) { - uint32 i, j; - - i = _midi->getBaseTempo(); - - j = _tempo = b; - j = j * 100 / _se->_tempoFactor; - - while (i & 0xFFFF0000 || j & 0xFFFF0000) { - i >>= 1; - j >>= 1; - } - - _tempo_eff = (i << 16) / j; - - set_speed(_speed); -} - -void Player::cancel_volume_fade() { - int i; - for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) { - if (_parameterFaders[i].param == ParameterFader::pfVolume) { - _parameterFaders[i].param = 0; - break; - } - } -} - -void Player::uninit_parts() { - if (_parts && _parts->_player != this) - error("asd"); - while (_parts) - _parts->uninit(); - - // In case another player is waiting to allocate parts - if (_midi) - _se->reallocateMidiChannels (_midi); -} - -void Player::uninit_seq() { - _abort = true; -} - -void Player::set_speed(byte speed) { - _speed = speed; - _timer_speed = (_tempo_eff * speed >> 7); -} - -byte *Player::parse_midi(byte *s) { - byte cmd, chan, note, velocity, control; - uint value; - Part *part; - - cmd = *s++; - - chan = cmd & 0xF; - - switch (cmd >> 4) { - case 0x8: // Key Off - note = *s++; - if (!_scanning) { - key_off(chan, note); - } else { - clear_active_note(chan, note); - } - s++; // Skip velocity - break; - - case 0x9: // Key On - note = *s++; - velocity = *s++; - if (velocity) { - if (!_scanning) - key_on(chan, note, velocity); - else - set_active_note(chan, note); - } else { - if (!_scanning) - key_off(chan, note); - else - clear_active_note(chan, note); - } - break; - - case 0xA: // Aftertouch - s += 2; - break; - - case 0xB: // Control Change - control = *s++; - value = *s++; - part = get_part(chan); - if (!part) - break; - - switch (control) { - case 1: // Modulation Wheel - part->set_modwheel(value); - break; - case 7: // Volume - part->set_vol(value); - break; - case 10: // Pan Position - part->set_pan(value - 0x40); - break; - case 16: // Pitchbend Factor (non-standard) - part->set_pitchbend_factor(value); - break; - case 17: // GP Slider 2 - part->set_detune(value - 0x40); - break; - case 18: // GP Slider 3 - part->set_pri(value - 0x40); - _se->reallocateMidiChannels (_midi); - break; - case 64: // Sustain Pedal - part->set_pedal(value != 0); - break; - case 91: // Effects Level - part->set_effect_level(value); - break; - case 93: // Chorus Level - part->set_chorus(value); - break; - default: - warning("parse_midi: invalid control %d", control); - } - break; - - case 0xC: // Program Change - value = *s++; - part = get_part(chan); - if (part) { - if (_isGM) { - if (value < 128) - part->set_program(value); - } else { - if (value < 32) - part->load_global_instrument(value); - } - } - break; - - case 0xD: // Channel Pressure - s++; - break; - - case 0xE: // Pitch Bend - part = get_part(chan); - if (part) - part->set_pitchbend(((s[1] << 7) | s[0]) - 0x2000); - s += 2; - break; - - case 0xF: - if (chan == 0) { - uint size = get_delta_time(&s); - parse_sysex(s, size); - s += size; - } else if (chan == 0xF) { - cmd = *s++; - if (cmd == 47) - goto Error; // End of song - if (cmd == 81) { - set_tempo((s[1] << 16) | (s[2] << 8) | s[3]); - s += 4; - break; - } - s += get_delta_time(&s); - } else if (chan == 0x7) { - s += get_delta_time(&s); - } else { - goto Error; - } - break; - - default: - Error:; - if (!_scanning) - clear(); - return NULL; - } - return s; -} - -void Player::parse_sysex(byte *p, uint len) { - byte code; - byte a; - uint b; - byte buf[128]; - Part *part; - - // 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) { - part->_instrument.roland (p - 1); - if (part->clearToTransmit()) - part->_instrument.send (part->_mc); - } - } else { - warning ("Unknown SysEx manufacturer 0x%02X", (int) a); - } - return; - } - --len; - - // Too big? - if (len >= sizeof(buf) * 2) - return; - -#ifdef IMUSE_DEBUG - for (a = 0; a < len + 1 && a < 19; ++a) { - sprintf ((char *)&buf[a*3], " %02X", p[a]); - } // next for - if (a < len + 1) { - buf[a*3] = buf[a*3+1] = buf[a*3+2] = '.'; - ++a; - } // end if - buf[a*3] = '\0'; - debug (0, "[%02d] SysEx:%s", _id, buf); -#endif - - switch (code = *p++) { - case 0: - if (g_scumm->_gameId != GID_SAMNMAX) { - // 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) - // BYTE 15: Program (upper 4 bits) - // BYTE 16: Program (lower 4 bits) - part = get_part (p[0] & 0x0F); - if (part) { - part->set_onoff (p[2] & 0x01); - part->set_vol ((p[5] & 0x0F) << 4 | (p[6] & 0x0F)); - part->_percussion = _isGM ? ((p[9] & 0x08) > 0) : false; - if (part->_percussion) { - if (part->_mc) { - part->off(); - _se->reallocateMidiChannels (_midi); - } - } else { - // Even in cases where a program does not seem to be specified, - // i.e. bytes 15 and 16 are 0, we send a program change because - // 0 is a valid program number. MI2 tests show that in such - // cases, a regular program change message always seems to follow - // anyway. - if (_isGM) - part->_instrument.program ((p[15] & 0x0F) << 4 | (p[16] & 0x0F), _mt32emulate); - part->sendAll(); - } - } - } else { - // Sam & Max: Trigger Event - // Triggers are set by doCommand (ImSetTrigger). - // When a SysEx marker is encountered whose sound - // ID and marker ID match what was set by ImSetTrigger, - // something magical is supposed to happen.... - for (a = 0; a < 16; ++a) { - if (_se->_snm_triggers [a].sound == _id && - _se->_snm_triggers [a].id == *p) - { - _se->_snm_triggers [a].sound = _se->_snm_triggers [a].id = 0; - _se->doCommand (_se->_snm_triggers [a].command [0], - _se->_snm_triggers [a].command [1], - _se->_snm_triggers [a].command [2], - _se->_snm_triggers [a].command [3], - 0, 0, 0, 0); - break; - } - } - } // end if - break; - - case 1: - // This SysEx is used in Sam & Max for maybe_jump. - if (_scanning) - break; - maybe_jump (p[0], p[1] - 1, (read_word (p + 2) - 1) * 4 + p[4], ((p[5] * _ticks_per_beat) >> 2) + p[6]); - break; - - case 2: // Start of song. Ignore for now. - break; - - case 16: // Adlib instrument definition (Part) - a = *p++ & 0x0F; - ++p; // Skip hardware type - part = get_part(a); - if (part) { - if (len == 63) { - decode_sysex_bytes(p, buf, len - 3); - part->set_instrument((byte *) buf); - } else { - // SPK tracks have len == 49 here, and are not supported - part->set_program (254); // Must be invalid, but not 255 (which is reserved) - } - } - break; - - case 17: // Adlib instrument definition (Global) - p += 2; // Skip hardware type and... whatever came right before it - a = *p++; - decode_sysex_bytes(p, buf, len - 4); - _se->setGlobalAdlibInstrument (a, buf); - break; - - case 33: // Parameter adjust - a = *p++ & 0x0F; - ++p; // Skip hardware type - decode_sysex_bytes(p, buf, len - 3); - part = get_part(a); - if (part) - part->set_param(read_word(buf), read_word(buf + 2)); - break; - - case 48: // Hook - jump - if (_scanning) - break; - decode_sysex_bytes(p + 1, buf, len - 2); - maybe_jump (buf[0], read_word (buf + 1), read_word (buf + 3), read_word (buf + 5)); - break; - - case 49: // Hook - global transpose - decode_sysex_bytes(p + 1, buf, len - 2); - maybe_set_transpose(buf); - break; - - case 50: // Hook - part on/off - buf[0] = *p++ & 0x0F; - decode_sysex_bytes(p, buf + 1, len - 2); - maybe_part_onoff(buf); - break; - - case 51: // Hook - set volume - buf[0] = *p++ & 0x0F; - decode_sysex_bytes(p, buf + 1, len - 2); - maybe_set_volume(buf); - break; - - case 52: // Hook - set program - buf[0] = *p++ & 0x0F; - decode_sysex_bytes(p, buf + 1, len - 2); - maybe_set_program(buf); - break; - - case 53: // Hook - set transpose - buf[0] = *p++ & 0x0F; - decode_sysex_bytes(p, buf + 1, len - 2); - maybe_set_transpose_part(buf); - break; - - case 64: // Marker - p++; - len -= 2; - while (len--) { - _se->handle_marker(_id, *p++); - } - break; - - case 80: // Loop - decode_sysex_bytes(p + 1, buf, len - 2); - set_loop(read_word(buf), - read_word(buf + 2), read_word(buf + 4), read_word(buf + 6), read_word(buf + 8) - ); - break; - - case 81: // End loop - clear_loop(); - break; - - case 96: // Set instrument - part = get_part(p[0] & 0x0F); - b = (p[1] & 0x0F) << 12 | (p[2] & 0x0F) << 8 | (p[4] & 0x0F) << 4 | (p[4] & 0x0F); - if (part) - part->set_instrument(b); - break; - - default: - warning ("Unknown SysEx command %d", (int) code); - } -} - -void Player::decode_sysex_bytes(byte *src, byte *dst, int len) { - while (len >= 0) { - *dst++ = (src[0] << 4) | (src[1] & 0xF); - src += 2; - len -= 2; - } -} - -void Player::maybe_jump (byte cmd, uint track, uint beat, uint tick) { - // Is this the hook I'm waiting for? - if (cmd && _hook._jump[0] != cmd) - return; - - // Reset hook? - if (cmd != 0 && cmd < 0x80) { - _hook._jump[0] = _hook._jump[1]; - _hook._jump[1] = 0; - } - - jump (track, beat, tick); -} - -void Player::maybe_set_transpose(byte *data) { - byte cmd; - - cmd = data[0]; - - // Is this the hook I'm waiting for? - if (cmd && _hook._transpose != cmd) - return; - - // Reset hook? - if (cmd != 0 && cmd < 0x80) - _hook._transpose = 0; - - set_transpose(data[1], (int8)data[2]); -} - -void Player::maybe_part_onoff(byte *data) { - byte cmd, *p; - uint chan; - Part *part; - - cmd = data[1]; - chan = data[0]; - - p = &_hook._part_onoff[chan]; - - // Is this the hook I'm waiting for? - if (cmd && *p != cmd) - return; - - if (cmd != 0 && cmd < 0x80) - *p = 0; - - part = get_part(chan); - if (part) - part->set_onoff(data[2] != 0); -} - -void Player::maybe_set_volume(byte *data) { - byte cmd; - byte *p; - uint chan; - Part *part; - - cmd = data[1]; - chan = data[0]; - - p = &_hook._part_volume[chan]; - - // Is this the hook I'm waiting for? - if (cmd && *p != cmd) - return; - - // Reset hook? - if (cmd != 0 && cmd < 0x80) - *p = 0; - - part = get_part(chan); - if (part) - part->set_vol(data[2]); -} - -void Player::maybe_set_program(byte *data) { - byte cmd; - byte *p; - uint chan; - Part *part; - - cmd = data[1]; - chan = data[0]; - - // Is this the hook I'm waiting for? - p = &_hook._part_program[chan]; - - if (cmd && *p != cmd) - return; - - if (cmd != 0 && cmd < 0x80) - *p = 0; - - part = get_part(chan); - if (part) - part->set_program(data[2]); -} - -void Player::maybe_set_transpose_part(byte *data) { - byte cmd; - byte *p; - uint chan; - - cmd = data[1]; - chan = data[0]; - - // Is this the hook I'm waiting for? - p = &_hook._part_transpose[chan]; - - if (cmd && *p != cmd) - return; - - // Reset hook? - if (cmd != 0 && cmd < 0x80) - *p = 0; - - part_set_transpose(chan, data[2], (int8)data[3]); -} - -int Player::set_transpose(byte relative, int b) { - Part *part; - - if (b > 24 || b < -24 || relative > 1) - return -1; - if (relative) - b = transpose_clamp(_transpose + b, -7, 7); - - _transpose = b; - - for (part = _parts; part; part = part->_next) { - part->set_transpose(part->_transpose); - } - - return 0; -} - -void Player::clear_active_notes() { - memset(_se->_active_notes, 0, sizeof(_se->_active_notes)); -} - -void Player::clear_active_note(int chan, byte note) { - _se->_active_notes[note] &= ~(1 << chan); -} - -void Player::set_active_note(int chan, byte note) { - _se->_active_notes[note] |= (1 << chan); -} - -void Player::part_set_transpose(uint8 chan, byte relative, int8 b) { - Part *part; - - if (b > 24 || b < -24) - return; - - part = get_part(chan); - if (!part) - return; - if (relative) - b = transpose_clamp(b + part->_transpose, -7, 7); - part->set_transpose(b); -} - -void Player::key_on(uint8 chan, uint8 note, uint8 velocity) { - Part *part; - - part = get_part(chan); - if (!part || !part->_on) - return; - - part->key_on(note, velocity); -} - -void Player::key_off(uint8 chan, uint8 note) { - Part *part; - - for (part = _parts; part; part = part->_next) { - if (part->_chan == (byte)chan && part->_on) - part->key_off(note); - } -} - -bool Player::jump(uint track, uint beat, uint tick) { - byte *mtrk, *cur_mtrk, *scanpos; - uint32 topos, curpos, track_offs; - - if (!_active) - return false; - - mtrk = _se->findTag(_song_index, "MTrk", track); - if (!mtrk) - return false; - - cur_mtrk = _se->findTag(_song_index, "MTrk", _track_index); - if (!cur_mtrk) - return false; - - if (beat == 0) - beat = 1; - - topos = (beat - 1) * _ticks_per_beat + tick; - - if (track == _track_index && topos >= _next_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) - return false; - curpos += get_delta_time(&scanpos); - } - - track_offs = scanpos - mtrk; - - turn_off_pedals(); - - find_sustaining_notes(cur_mtrk + _song_offset, mtrk + track_offs, curpos - topos); - - _beat_index = beat; - _tick_index = tick; - _cur_pos = topos; - _next_pos = curpos; - _timer_counter = 0; - _song_offset = track_offs; - if (track != _track_index) { - _track_index = track; - _loop_counter = 0; - } - _abort = true; - return true; -} - -bool Player::set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick) { - if (tobeat + 1 >= frombeat) - return false; - - if (tobeat == 0) - tobeat = 1; - - _loop_counter = 0; // Because of possible interrupts - _loop_to_beat = tobeat; - _loop_to_tick = totick; - _loop_from_beat = frombeat; - _loop_from_tick = fromtick; - _loop_counter = count; - - return true; -} - -void Player::clear_loop() { - _loop_counter = 0; -} - -void Player::turn_off_pedals() { - Part *part; - - for (part = _parts; part; part = part->_next) { - if (part->_pedal) - part->set_pedal(false); - } -} - -void Player::find_sustaining_notes(byte *a, byte *b, uint32 l) { - uint32 pos; - uint16 mask; - uint16 *bitlist_ptr; - SustainingNotes *sn, *next; - IsNoteCmdData isnote; - int j; - uint num_active; - uint max_off_pos; - - num_active = update_actives(); - - // pos contains number of ticks since current position - pos = _next_pos - _cur_pos; - if ((int32)pos < 0) - pos = 0; - - // Locate the positions where the notes are turned off. - // Remember each note that was turned off. - while (num_active != 0) { - // Is note off? - j = is_note_cmd(&a, &isnote); - if (j == -1) - break; - if (j == 1) { - mask = 1 << isnote.chan; - bitlist_ptr = _se->_active_notes + isnote.note; - if (*bitlist_ptr & mask) { - *bitlist_ptr &= ~mask; - num_active--; - // Get a node from the free list - if ((sn = _se->_sustain_notes_free) == NULL) - return; - _se->_sustain_notes_free = sn->next; - - // Insert it in the beginning of the used list - sn->next = _se->_sustain_notes_used; - _se->_sustain_notes_used = sn; - sn->prev = NULL; - if (sn->next) - sn->next->prev = sn; - - sn->note = isnote.note; - sn->chan = isnote.chan; - sn->player = this; - sn->off_pos = pos; - sn->pos = 0; - sn->counter = 0; - } - } - pos += get_delta_time(&a); - } - - // Find the maximum position where a note was turned off - max_off_pos = 0; - for (sn = _se->_sustain_notes_used; sn; sn = sn->next) { - _se->_active_notes[sn->note] |= (1 << sn->chan); - if (sn->off_pos > max_off_pos) { - max_off_pos = sn->off_pos; - } - } - - // locate positions where notes are turned on - pos = l; - while (pos < max_off_pos) { - j = is_note_cmd(&b, &isnote); - if (j == -1) - break; - if (j == 2) { - mask = 1 << isnote.chan; - bitlist_ptr = _se->_active_notes + isnote.note; - - if (*bitlist_ptr & mask) { - sn = _se->_sustain_notes_used; - while (sn) { - next = sn->next; - if (sn->note == isnote.note && sn->chan == isnote.chan && pos < sn->off_pos) { - *bitlist_ptr &= ~mask; - // Unlink from the sustain list - if (next) - next->prev = sn->prev; - if (sn->prev) - sn->prev->next = next; - else - _se->_sustain_notes_used = next; - // Insert into the free list - sn->next = _se->_sustain_notes_free; - _se->_sustain_notes_free = sn; - } - sn = next; - } - } - } - pos += get_delta_time(&b); - } - - // Concatenate head and used list - if (!_se->_sustain_notes_head) { - _se->_sustain_notes_head = _se->_sustain_notes_used; - _se->_sustain_notes_used = NULL; - return; - } - sn = _se->_sustain_notes_head; - while (sn->next) - sn = sn->next; - sn->next = _se->_sustain_notes_used; - _se->_sustain_notes_used = NULL; - if (sn->next) - sn->next->prev = sn; -} - -Part *Player::get_part(uint8 chan) { - Part *part; - - part = _parts; - while (part) { - if (part->_chan == chan) - return part; - part = part->_next; - } - - part = _se->allocate_part (_priority, _midi); - if (!part) { - warning("no parts available"); - return NULL; - } - - part->_chan = chan; - part->setup(this); - - return part; -} - -uint Player::update_actives() { - Part *part; - uint16 *active; - int count = 0; - - clear_active_notes(); - active = _se->_active_notes; - for (part = _parts; part; part = part->_next) { - if (part->_mc) - count += part->update_actives(active); - } - return count; -} - -void Player::set_priority(int pri) { - Part *part; - - _priority = pri; - for (part = _parts; part; part = part->_next) { - part->set_pri(part->_pri); - } - _se->reallocateMidiChannels (_midi); -} - -void Player::set_pan(int pan) { - Part *part; - - _pan = pan; - for (part = _parts; part; part = part->_next) { - part->set_pan(part->_pan); - } -} - -void Player::set_detune(int detune) { - Part *part; - - _detune = detune; - for (part = _parts; part; part = part->_next) { - part->set_detune(part->_detune); - } -} - -int Player::scan(uint totrack, uint tobeat, uint totick) { - byte *mtrk, *scanptr; - uint32 curpos, topos; - uint32 pos; - - if (!_active) - return -1; - - mtrk = _se->findTag(_song_index, "MTrk", totrack); - if (!mtrk) - return -1; - - if (tobeat == 0) - tobeat++; - - turn_off_parts(); - clear_active_notes(); - scanptr = mtrk; - curpos = get_delta_time(&scanptr); - _scanning = true; - - topos = (tobeat - 1) * _ticks_per_beat + totick; - - while (curpos < topos) { - scanptr = parse_midi(scanptr); - if (!scanptr) { - _scanning = false; - return -1; - } - curpos += get_delta_time(&scanptr); - } - pos = scanptr - mtrk; - - _scanning = false; - _se->reallocateMidiChannels (_midi); - 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; - } - return 0; -} - -void Player::turn_off_parts() { - Part *part; - - for (part = _parts; part; part = part->_next) - part->off(); - _se->reallocateMidiChannels (_midi); -} - -void Player::play_active_notes() { - int i, j; - uint mask; - - for (i = 0; i != 128; i++) { - mask = _se->_active_notes[i]; - for (j = 0; j != 16; j++, mask >>= 1) { - if (mask & 1) { - key_on(j, i, 80); - } - } - } -} - -int Player::set_vol(byte vol) { - Part *part; - - if (vol > 127) - return -1; - - _volume = vol; - _vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7; - - for (part = _parts; part; part = part->_next) { - part->set_vol(part->_vol); - } - - return 0; -} - -int Player::get_param(int param, byte chan) { - switch (param) { - case 0: - return (byte)_priority; - case 1: - return (byte)_volume; - case 2: - return (byte)_pan; - case 3: - return (byte)_transpose; - case 4: - return (byte)_detune; - case 5: - return _speed; - case 6: - return _track_index; - case 7: - return _beat_index; - case 8: - return _tick_index; - case 9: - return _loop_counter; - case 10: - return _loop_to_beat; - case 11: - return _loop_to_tick; - case 12: - return _loop_from_beat; - case 13: - return _loop_from_tick; - case 14: - case 15: - case 16: - case 17: - return query_part_param(param, chan); - case 18: - case 19: - case 20: - case 21: - case 22: - case 23: - return _hook.query_param(param, chan); - default: - return -1; - } -} - -int Player::query_part_param(int param, byte chan) { - Part *part; - - part = _parts; - while (part) { - if (part->_chan == chan) { - switch (param) { - case 14: - return part->_on; - case 15: - return part->_vol; - case 16: - return (int) part->_instrument; - case 17: - return part->_transpose; - default: - return -1; - } - } - part = part->_next; - } - return 129; -} - -void Player::sequencer_timer() { - byte *mtrk; - uint32 counter; - byte *song_ptr; - - // First handle any parameter transitions - // that are occuring. - transitionParameters(); - - // Since the volume parameter can cause - // the player to be deactivated, check - // to make sure we're still active. - if (!_active) - return; - - counter = _timer_counter + _timer_speed; - _timer_counter = counter & 0xFFFF; - _cur_pos += counter >> 16; - _tick_index += counter >> 16; - - if (_tick_index >= _ticks_per_beat) { - _beat_index += _tick_index / _ticks_per_beat; - _tick_index %= _ticks_per_beat; - } - if (_loop_counter && _beat_index >= _loop_from_beat && _tick_index >= _loop_from_tick) { - _loop_counter--; - jump(_track_index, _loop_to_beat, _loop_to_tick); - } - if (_next_pos <= _cur_pos) { - mtrk = _se->findTag(_song_index, "MTrk", _track_index); - if (!mtrk) { - warning("Sound %d was unloaded while active", _song_index); - clear(); - } else { - song_ptr = mtrk + _song_offset; - _abort = false; - - while (_next_pos <= _cur_pos) { - song_ptr = parse_midi(song_ptr); - if (!song_ptr || _abort) - return; - _next_pos += get_delta_time(&song_ptr); - _song_offset = song_ptr - mtrk; - } - } - } -} - void IMuseInternal::handleDeferredCommands (MidiDriver *midi) { uint32 advance = midi->getBaseTempo() / 500; @@ -2838,111 +1199,10 @@ void IMuseInternal::addDeferredCommand (int time, int a, int b, int c, int d, in } } -// "time" is referenced as hundredths of a second. -// IS THAT CORRECT?? -// We convert it to microseconds before prceeding -int Player::addParameterFader (int param, int target, int time) { - int start; - - switch (param) { - case ParameterFader::pfVolume: - // Volume fades are handled differently. - start = _volume; - break; - - case ParameterFader::pfTranspose: - // FIXME: Is this transpose? And what's the scale? - // It's set to fade to -2400 in the tunnel of love. - warning ("parameterTransition(3) outside Tunnel of Love?"); - start = _transpose; - target /= 200; - break; - - case ParameterFader::pfSpeed: - // FIXME: Is the speed from 0-100? - // Right now I convert it to 0-128. - start = _speed; - target = target * 128 / 100; - break; - - case 127: - // FIXME: This MIGHT fade ALL supported parameters, - // but I'm not sure. - return 0; - - default: - warning ("Player::addParameterFader(): Unknown parameter %d", param); - return 0; // Should be -1, but we'll let the script think it worked. - } - - ParameterFader *ptr = &_parameterFaders[0]; - ParameterFader *best = 0; - int i; - for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) { - if (ptr->param == param) { - best = ptr; - start = ptr->end; - break; - } else if (!ptr->param) { - best = ptr; - } - } - - if (best) { - best->param = param; - best->start = start; - best->end = target; - best->total_time = (uint32) time * 10000; - best->current_time = 0; - } else { - warning ("IMuse Player %d: Out of parameter faders", _id); - return -1; - } - - return 0; -} - -void Player::transitionParameters() { - uint32 advance = _midi->getBaseTempo() / 500; - int value; - - ParameterFader *ptr = &_parameterFaders[0]; - int i; - for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) { - if (!ptr->param) - continue; - - ptr->current_time += advance; - if (ptr->current_time > ptr->total_time) - ptr->current_time = ptr->total_time; - value = (int32) ptr->start + (int32) (ptr->end - ptr->start) * (int32) ptr->current_time / (int32) ptr->total_time; - - switch (ptr->param) { - case ParameterFader::pfVolume: - // Volume. - if (!value) { - clear(); - return; - } - set_vol ((byte) value); - break; - - case ParameterFader::pfSpeed: - // Speed. - set_speed ((byte) value); - break; - - case ParameterFader::pfTranspose: - // FIXME: Is this really transpose? - set_transpose (0, value); - break; - } - - if (ptr->current_time >= ptr->total_time) - ptr->param = 0; - } -} - +//////////////////////////////////////////////////////////// +// +// IMuseInternal load/save implementation +// //////////////////////////////////////////////////////////// enum { diff --git a/scumm/imuse_internal.h b/scumm/imuse_internal.h new file mode 100644 index 0000000000..6ff5c7b02b --- /dev/null +++ b/scumm/imuse_internal.h @@ -0,0 +1,460 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#ifndef DEFINED_IMUSE_INTERNAL +#define DEFINED_IMUSE_INTERNAL + +#include "common/scummsys.h" +#include "instrument.h" + +struct HookDatas; +struct ParameterFader; +struct DeferredCommand; +struct ImTrigger; +struct SustainingNotes; +struct CommandQueue; +struct IsNoteCmdData; +struct Player; +struct Part; +class IMuseInternal; + +// Some entities also referenced +class MidiDriver; +class MidiChannel; +class Scumm; +class OSystem; + + + +////////////////////////////////////////////////// +// +// Some constants +// +////////////////////////////////////////////////// + +#define TICKS_PER_BEAT 480 + +#define IMUSE_SYSEX_ID 0x7D +#define ROLAND_SYSEX_ID 0x41 +#define PERCUSSION_CHANNEL 9 + +#define TRIGGER_ID 0 +#define COMMAND_ID 1 + +#define MDPG_TAG "MDpg" +#define MDHD_TAG "MDhd" + + + +//////////////////////////////////////// +// +// Helper functions +// +//////////////////////////////////////// + +static int clamp(int val, int min, int max) { + if (val < min) + return min; + if (val > max) + return max; + return val; +} + +static int transpose_clamp(int a, int b, int c) { + if (b > a) + a += (b - a + 11) / 12 * 12; + if (c < a) + a -= (a - c + 11) / 12 * 12; + return a; +} + + + +////////////////////////////////////////////////// +// +// Entity declarations +// +////////////////////////////////////////////////// + +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); +}; + +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 { + MidiDriver *midi; + uint32 time_left; + int a, b, c, d, e, f; +}; + +struct ImTrigger { + int sound; + byte id; + uint16 expire; + byte command [4]; +}; + +struct SustainingNotes { + SustainingNotes *next; + SustainingNotes *prev; + Player *player; + byte note, chan; + uint32 off_pos; + uint32 pos; + uint16 counter; +}; + +struct CommandQueue { + uint16 array[8]; +}; + +struct IsNoteCmdData { + byte chan; + byte note; + byte vel; +}; + +struct Player { + IMuseInternal *_se; + MidiDriver *_midi; + + Part *_parts; + bool _active; + bool _scanning; + int _id; + byte _priority; + byte _volume; + int8 _pan; + int8 _transpose; + int8 _detune; + uint _vol_chan; + byte _vol_eff; + + uint _song_index; + uint _track_index; + uint _timer_counter; + uint _loop_to_beat; + uint _loop_from_beat; + uint _loop_counter; + uint _loop_to_tick; + uint _loop_from_tick; + uint32 _tempo; + uint32 _tempo_eff; // No Save + uint32 _cur_pos; + uint32 _next_pos; + uint32 _song_offset; + uint32 _timer_speed; // No Save + uint _tick_index; + uint _beat_index; + uint _ticks_per_beat; + byte _speed; // No Save + bool _abort; + + HookDatas _hook; + ParameterFader _parameterFaders[4]; + + bool _mt32emulate; + bool _isGM; + + // Player part + void hook_clear(); + void clear(); + bool startSound (int sound, MidiDriver *midi); + void uninit_parts(); + byte *parse_midi(byte *s); + void key_off(uint8 chan, byte data); + void key_on(uint8 chan, byte data, byte velocity); + void part_set_transpose(uint8 chan, byte relative, int8 b); + void parse_sysex(byte *p, uint len); + void maybe_jump (byte cmd, uint track, uint beat, uint tick); + void maybe_set_transpose(byte *data); + void maybe_part_onoff(byte *data); + void maybe_set_volume(byte *data); + void maybe_set_program(byte *data); + void maybe_set_transpose_part(byte *data); + uint update_actives(); + Part *get_part(uint8 part); + void turn_off_pedals(); + int set_vol(byte vol); + int get_param(int param, byte chan); + int query_part_param(int param, byte chan); + int set_transpose(byte relative, int b); + void set_priority(int pri); + void set_pan(int pan); + void set_detune(int detune); + void turn_off_parts(); + void play_active_notes(); + void cancel_volume_fade(); + + int addParameterFader (int param, int target, int time); + void transitionParameters(); + + static void decode_sysex_bytes(byte *src, byte *dst, int len); + + void clear_active_note(int chan, byte note); + void set_active_note(int chan, byte note); + void clear_active_notes(); + + // Sequencer part + bool set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick); + void clear_loop(); + void set_speed(byte speed); + bool jump(uint track, uint beat, uint tick); + void uninit_seq(); + void set_tempo(uint32 data); + int start_seq_sound(int sound); + void find_sustaining_notes(byte *a, byte *b, uint32 l); + int scan(uint totrack, uint tobeat, uint totick); + int query_param(int param); + + bool is_fading_out(); + void sequencer_timer(); + + Player() { + memset(this,0,sizeof(Player)); + } + +}; + +struct Part { + 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 + + // Used to be in MidiDriver + uint16 _actives[8]; + + void key_on(byte note, byte velocity); + void key_off(byte note); + void set_param(byte param, int value) { } + void init(); + void setup(Player *player); + void uninit(); + void off(); + void silence(); + void set_instrument(uint b); + void set_instrument(byte *data); + void load_global_instrument (byte b); + + void set_transpose(int8 transpose); + void set_vol(uint8 volume); + void set_detune(int8 detune); + void set_pri(int8 pri); + void set_pan(int8 pan); + void set_modwheel(uint value); + void set_pedal(bool value); + void set_pitchbend(int value); + void release_pedal(); + void set_program(byte program); + void set_chorus(uint chorus); + void set_effect_level(uint level); + + int update_actives(uint16 *active); + void set_pitchbend_factor(uint8 value); + void set_onoff(bool on); + void fix_after_load(); + + void sendAll(); + bool clearToTransmit(); + + Part() { + memset(this,0,sizeof(Part)); + } +}; + +// WARNING: This is the internal variant of the IMUSE class. +// imuse.h contains a public version of the same class. +// the public version, only contains a set of methods. +class IMuseInternal { + friend struct Player; + +private: + bool _old_adlib_instruments; + bool _enable_multi_midi; + MidiDriver *_midi_adlib; + MidiDriver *_midi_native; + + byte **_base_sounds; + +private: + bool _paused; + bool _initialized; + + int _tempoFactor; + + uint _queue_end, _queue_pos, _queue_sound; + byte _queue_adding; + + SustainingNotes *_sustain_notes_used; + SustainingNotes *_sustain_notes_free; + SustainingNotes *_sustain_notes_head; + + byte _queue_marker; + byte _queue_cleared; + byte _master_volume; // Master volume. 0-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]; + SustainingNotes _sustaining_notes[24]; + Part _parts[32]; + + uint16 _active_notes[128]; + Instrument _global_adlib_instruments[32]; + CommandQueue _cmd_queue[64]; + DeferredCommand _deferredCommands[4]; + + byte *findTag(int sound, char *tag, int index); + bool isMT32(int sound); + bool isGM(int sound); + int get_queue_sound_status(int sound); + void handle_marker(uint id, byte data); + int get_channel_volume(uint a); + void initMidiDriver (MidiDriver *midi); + void init_players(); + void init_parts(); + void init_sustaining_notes(); + void init_queue(); + + void sequencer_timers (MidiDriver *midi); + void expire_sustain_notes (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); + 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 query_queue(int param); + Player *get_player_byid(int id); + + int get_volchan_entry(uint a); + int set_volchan_entry(uint a, uint b); + int set_channel_volume(uint chan, uint vol); + void update_volumes(); + void reset_tick(); + + int set_volchan(int sound, int volchan); + + void fix_parts_after_load(); + void fix_players_after_load(Scumm *scumm); + + static int saveReference(void *me_ref, byte type, void *ref); + static void *loadReference(void *me_ref, byte type, int ref); + + static void midiTimerCallback (void *data); + +public: + IMuseInternal() { + memset(this,0,sizeof(IMuseInternal)); + } + ~IMuseInternal(); + + int initialize(OSystem *syst, MidiDriver *midi); + void reallocateMidiChannels (MidiDriver *midi); + void setGlobalAdlibInstrument (byte slot, byte *data); + void copyGlobalAdlibInstrument (byte slot, Instrument *dest); + + // IMuse interface + + void on_timer (MidiDriver *midi); + void pause(bool paused); + int terminate(); + int save_or_load(Serializer *ser, Scumm *scumm); + int set_music_volume(uint vol); + int get_music_volume(); + int set_master_volume(uint vol); + int get_master_volume(); + bool startSound(int sound); + int stopSound(int sound); + int stop_all_sounds(); + int getSoundStatus(int sound); + bool get_sound_active(int sound); + int32 doCommand(int a, int b, int c, int d, int e, int f, int g, int h); + int clear_queue(); + void setBase(byte **base); + + uint32 property(int prop, uint32 value); + MidiDriver *getMidiDriver(); + + static IMuseInternal *create(OSystem *syst, MidiDriver *midi); +}; + +#endif diff --git a/scumm/imuse_player.cpp b/scumm/imuse_player.cpp new file mode 100644 index 0000000000..0b89070659 --- /dev/null +++ b/scumm/imuse_player.cpp @@ -0,0 +1,1377 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2003 The ScummVM project + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * $Header$ + */ + +#include "stdafx.h" +#include "scumm/scumm.h" +#include "sound/mididrv.h" +#include "common/util.h" +#include "common/engine.h" +#include "imuse_internal.h" + + + +//////////////////////////////////////// +// +// Helper functions +// +//////////////////////////////////////// + +static uint32 get_delta_time(byte **s) { + byte *d = *s, b; + uint32 time = 0; + do { + b = *d++; + time = (time << 7) | (b & 0x7F); + } while (b & 0x80); + *s = d; + return time; +} + +static uint read_word(byte *a) { + return (a[0] << 8) + a[1]; +} + +static void skip_midi_cmd(byte **song_ptr) { + byte *s, code; + + const byte num_skip[] = { + 2, 2, 2, 2, 1, 1, 2 + }; + + s = *song_ptr; + + code = *s++; + + if (code < 0x80) { + s = NULL; + } else if (code < 0xF0) { + s += num_skip[(code & 0x70) >> 4]; + } else { + if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) { + s += get_delta_time(&s); + } else { + s = NULL; + } + } + *song_ptr = s; +} + +static int is_note_cmd(byte **a, IsNoteCmdData * isnote) { + byte *s = *a; + byte code; + + code = *s++; + + switch (code >> 4) { + case 8: // Key Off + isnote->chan = code & 0xF; + isnote->note = *s++; + isnote->vel = *s++; + *a = s; + return 1; + case 9: // Key On + isnote->chan = code & 0xF; + isnote->note = *s++; + isnote->vel = *s++; + *a = s; + if (isnote->vel) + return 2; + return 1; + case 0xA: + case 0xB: + case 0xE: + s++; + case 0xC: + case 0xD: + s++; + break; + case 0xF: + if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) { + s += get_delta_time(&s); + break; + } + return -1; + default: + return -1; + } + *a = s; + return 0; +} + + + +////////////////////////////////////////////////// +// +// IMuse Player implementation +// +////////////////////////////////////////////////// + +bool Player::is_fading_out() { + int i; + for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) { + if (_parameterFaders[i].param == ParameterFader::pfVolume && + _parameterFaders[i].end == 0) + { + return true; + } + } + return false; +} + +void Player::clear() { + uninit_seq(); + cancel_volume_fade(); + uninit_parts(); + _se->ImFireAllTriggers (_id); + _active = false; + _ticks_per_beat = TICKS_PER_BEAT; + _midi = NULL; +} + +bool Player::startSound (int sound, MidiDriver *midi) { + void *mdhd; + int i; + + mdhd = _se->findTag(sound, MDHD_TAG, 0); + if (mdhd == NULL) { + mdhd = _se->findTag(sound, MDPG_TAG, 0); + if (mdhd == NULL) { + warning("P::startSound failed: Couldn't find %s", MDHD_TAG); + return false; + } + } + + _mt32emulate = _se->isMT32(sound); + _isGM = _se->isGM(sound); + + _parts = NULL; + _active = true; + _midi = midi; + _id = sound; + _priority = 0x80; + _volume = 0x7F; + _vol_chan = 0xFFFF; + _vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7; + _pan = 0; + _transpose = 0; + _detune = 0; + + for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) + _parameterFaders[i].init(); + + hook_clear(); + if (start_seq_sound(sound) != 0) { + _active = false; + _midi = NULL; + return false; + } + return true; +} + +void Player::hook_clear() { + memset(&_hook, 0, sizeof(_hook)); +} + +int Player::start_seq_sound(int sound) { + byte *ptr, *track_ptr; + + _song_index = sound; + _timer_counter = 0; + _loop_to_beat = 1; + _loop_from_beat = 1; + _track_index = 0; + _loop_counter = 0; + _loop_to_tick = 0; + _loop_from_tick = 0; + + set_tempo(500000); + set_speed(128); + ptr = _se->findTag(sound, "MTrk", _track_index); + if (ptr == NULL) + return -1; + + track_ptr = ptr; + _cur_pos = _next_pos = get_delta_time(&track_ptr); + _song_offset = track_ptr - ptr; + + _tick_index = _cur_pos; + _beat_index = 1; + + if (_tick_index >= _ticks_per_beat) { + _beat_index += _tick_index / _ticks_per_beat; + _tick_index %= _ticks_per_beat; + } + + return 0; +} + +void Player::set_tempo(uint32 b) { + uint32 i, j; + + i = _midi->getBaseTempo(); + + j = _tempo = b; + j = j * 100 / _se->_tempoFactor; + + while (i & 0xFFFF0000 || j & 0xFFFF0000) { + i >>= 1; + j >>= 1; + } + + _tempo_eff = (i << 16) / j; + + set_speed(_speed); +} + +void Player::cancel_volume_fade() { + int i; + for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) { + if (_parameterFaders[i].param == ParameterFader::pfVolume) { + _parameterFaders[i].param = 0; + break; + } + } +} + +void Player::uninit_parts() { + if (_parts && _parts->_player != this) + error("asd"); + while (_parts) + _parts->uninit(); + + // In case another player is waiting to allocate parts + if (_midi) + _se->reallocateMidiChannels (_midi); +} + +void Player::uninit_seq() { + _abort = true; +} + +void Player::set_speed(byte speed) { + _speed = speed; + _timer_speed = (_tempo_eff * speed >> 7); +} + +byte *Player::parse_midi(byte *s) { + byte cmd, chan, note, velocity, control; + uint value; + Part *part; + + cmd = *s++; + + chan = cmd & 0xF; + + switch (cmd >> 4) { + case 0x8: // Key Off + note = *s++; + if (!_scanning) { + key_off(chan, note); + } else { + clear_active_note(chan, note); + } + s++; // Skip velocity + break; + + case 0x9: // Key On + note = *s++; + velocity = *s++; + if (velocity) { + if (!_scanning) + key_on(chan, note, velocity); + else + set_active_note(chan, note); + } else { + if (!_scanning) + key_off(chan, note); + else + clear_active_note(chan, note); + } + break; + + case 0xA: // Aftertouch + s += 2; + break; + + case 0xB: // Control Change + control = *s++; + value = *s++; + part = get_part(chan); + if (!part) + break; + + switch (control) { + case 1: // Modulation Wheel + part->set_modwheel(value); + break; + case 7: // Volume + part->set_vol(value); + break; + case 10: // Pan Position + part->set_pan(value - 0x40); + break; + case 16: // Pitchbend Factor (non-standard) + part->set_pitchbend_factor(value); + break; + case 17: // GP Slider 2 + part->set_detune(value - 0x40); + break; + case 18: // GP Slider 3 + part->set_pri(value - 0x40); + _se->reallocateMidiChannels (_midi); + break; + case 64: // Sustain Pedal + part->set_pedal(value != 0); + break; + case 91: // Effects Level + part->set_effect_level(value); + break; + case 93: // Chorus Level + part->set_chorus(value); + break; + default: + warning("parse_midi: invalid control %d", control); + } + break; + + case 0xC: // Program Change + value = *s++; + part = get_part(chan); + if (part) { + if (_isGM) { + if (value < 128) + part->set_program(value); + } else { + if (value < 32) + part->load_global_instrument(value); + } + } + break; + + case 0xD: // Channel Pressure + s++; + break; + + case 0xE: // Pitch Bend + part = get_part(chan); + if (part) + part->set_pitchbend(((s[1] << 7) | s[0]) - 0x2000); + s += 2; + break; + + case 0xF: + if (chan == 0) { + uint size = get_delta_time(&s); + parse_sysex(s, size); + s += size; + } else if (chan == 0xF) { + cmd = *s++; + if (cmd == 47) + goto Error; // End of song + if (cmd == 81) { + set_tempo((s[1] << 16) | (s[2] << 8) | s[3]); + s += 4; + break; + } + s += get_delta_time(&s); + } else if (chan == 0x7) { + s += get_delta_time(&s); + } else { + goto Error; + } + break; + + default: + Error:; + if (!_scanning) + clear(); + return NULL; + } + return s; +} + +void Player::parse_sysex(byte *p, uint len) { + byte code; + byte a; + uint b; + byte buf[128]; + Part *part; + + // 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) { + part->_instrument.roland (p - 1); + if (part->clearToTransmit()) + part->_instrument.send (part->_mc); + } + } else { + warning ("Unknown SysEx manufacturer 0x%02X", (int) a); + } + return; + } + --len; + + // Too big? + if (len >= sizeof(buf) * 2) + return; + +#ifdef IMUSE_DEBUG + for (a = 0; a < len + 1 && a < 19; ++a) { + sprintf ((char *)&buf[a*3], " %02X", p[a]); + } // next for + if (a < len + 1) { + buf[a*3] = buf[a*3+1] = buf[a*3+2] = '.'; + ++a; + } // end if + buf[a*3] = '\0'; + debug (0, "[%02d] SysEx:%s", _id, buf); +#endif + + switch (code = *p++) { + case 0: + if (g_scumm->_gameId != GID_SAMNMAX) { + // 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) + // BYTE 15: Program (upper 4 bits) + // BYTE 16: Program (lower 4 bits) + part = get_part (p[0] & 0x0F); + if (part) { + part->set_onoff (p[2] & 0x01); + part->set_vol ((p[5] & 0x0F) << 4 | (p[6] & 0x0F)); + part->_percussion = _isGM ? ((p[9] & 0x08) > 0) : false; + if (part->_percussion) { + if (part->_mc) { + part->off(); + _se->reallocateMidiChannels (_midi); + } + } else { + // Even in cases where a program does not seem to be specified, + // i.e. bytes 15 and 16 are 0, we send a program change because + // 0 is a valid program number. MI2 tests show that in such + // cases, a regular program change message always seems to follow + // anyway. + if (_isGM) + part->_instrument.program ((p[15] & 0x0F) << 4 | (p[16] & 0x0F), _mt32emulate); + part->sendAll(); + } + } + } else { + // Sam & Max: Trigger Event + // Triggers are set by doCommand (ImSetTrigger). + // When a SysEx marker is encountered whose sound + // ID and marker ID match what was set by ImSetTrigger, + // something magical is supposed to happen.... + for (a = 0; a < 16; ++a) { + if (_se->_snm_triggers [a].sound == _id && + _se->_snm_triggers [a].id == *p) + { + _se->_snm_triggers [a].sound = _se->_snm_triggers [a].id = 0; + _se->doCommand (_se->_snm_triggers [a].command [0], + _se->_snm_triggers [a].command [1], + _se->_snm_triggers [a].command [2], + _se->_snm_triggers [a].command [3], + 0, 0, 0, 0); + break; + } + } + } // end if + break; + + case 1: + // This SysEx is used in Sam & Max for maybe_jump. + if (_scanning) + break; + maybe_jump (p[0], p[1] - 1, (read_word (p + 2) - 1) * 4 + p[4], ((p[5] * _ticks_per_beat) >> 2) + p[6]); + break; + + case 2: // Start of song. Ignore for now. + break; + + case 16: // Adlib instrument definition (Part) + a = *p++ & 0x0F; + ++p; // Skip hardware type + part = get_part(a); + if (part) { + if (len == 63) { + decode_sysex_bytes(p, buf, len - 3); + part->set_instrument((byte *) buf); + } else { + // SPK tracks have len == 49 here, and are not supported + part->set_program (254); // Must be invalid, but not 255 (which is reserved) + } + } + break; + + case 17: // Adlib instrument definition (Global) + p += 2; // Skip hardware type and... whatever came right before it + a = *p++; + decode_sysex_bytes(p, buf, len - 4); + _se->setGlobalAdlibInstrument (a, buf); + break; + + case 33: // Parameter adjust + a = *p++ & 0x0F; + ++p; // Skip hardware type + decode_sysex_bytes(p, buf, len - 3); + part = get_part(a); + if (part) + part->set_param(read_word(buf), read_word(buf + 2)); + break; + + case 48: // Hook - jump + if (_scanning) + break; + decode_sysex_bytes(p + 1, buf, len - 2); + maybe_jump (buf[0], read_word (buf + 1), read_word (buf + 3), read_word (buf + 5)); + break; + + case 49: // Hook - global transpose + decode_sysex_bytes(p + 1, buf, len - 2); + maybe_set_transpose(buf); + break; + + case 50: // Hook - part on/off + buf[0] = *p++ & 0x0F; + decode_sysex_bytes(p, buf + 1, len - 2); + maybe_part_onoff(buf); + break; + + case 51: // Hook - set volume + buf[0] = *p++ & 0x0F; + decode_sysex_bytes(p, buf + 1, len - 2); + maybe_set_volume(buf); + break; + + case 52: // Hook - set program + buf[0] = *p++ & 0x0F; + decode_sysex_bytes(p, buf + 1, len - 2); + maybe_set_program(buf); + break; + + case 53: // Hook - set transpose + buf[0] = *p++ & 0x0F; + decode_sysex_bytes(p, buf + 1, len - 2); + maybe_set_transpose_part(buf); + break; + + case 64: // Marker + p++; + len -= 2; + while (len--) { + _se->handle_marker(_id, *p++); + } + break; + + case 80: // Loop + decode_sysex_bytes(p + 1, buf, len - 2); + set_loop(read_word(buf), + read_word(buf + 2), read_word(buf + 4), read_word(buf + 6), read_word(buf + 8) + ); + break; + + case 81: // End loop + clear_loop(); + break; + + case 96: // Set instrument + part = get_part(p[0] & 0x0F); + b = (p[1] & 0x0F) << 12 | (p[2] & 0x0F) << 8 | (p[4] & 0x0F) << 4 | (p[4] & 0x0F); + if (part) + part->set_instrument(b); + break; + + default: + warning ("Unknown SysEx command %d", (int) code); + } +} + +void Player::decode_sysex_bytes(byte *src, byte *dst, int len) { + while (len >= 0) { + *dst++ = (src[0] << 4) | (src[1] & 0xF); + src += 2; + len -= 2; + } +} + +void Player::maybe_jump (byte cmd, uint track, uint beat, uint tick) { + // Is this the hook I'm waiting for? + if (cmd && _hook._jump[0] != cmd) + return; + + // Reset hook? + if (cmd != 0 && cmd < 0x80) { + _hook._jump[0] = _hook._jump[1]; + _hook._jump[1] = 0; + } + + jump (track, beat, tick); +} + +void Player::maybe_set_transpose(byte *data) { + byte cmd; + + cmd = data[0]; + + // Is this the hook I'm waiting for? + if (cmd && _hook._transpose != cmd) + return; + + // Reset hook? + if (cmd != 0 && cmd < 0x80) + _hook._transpose = 0; + + set_transpose(data[1], (int8)data[2]); +} + +void Player::maybe_part_onoff(byte *data) { + byte cmd, *p; + uint chan; + Part *part; + + cmd = data[1]; + chan = data[0]; + + p = &_hook._part_onoff[chan]; + + // Is this the hook I'm waiting for? + if (cmd && *p != cmd) + return; + + if (cmd != 0 && cmd < 0x80) + *p = 0; + + part = get_part(chan); + if (part) + part->set_onoff(data[2] != 0); +} + +void Player::maybe_set_volume(byte *data) { + byte cmd; + byte *p; + uint chan; + Part *part; + + cmd = data[1]; + chan = data[0]; + + p = &_hook._part_volume[chan]; + + // Is this the hook I'm waiting for? + if (cmd && *p != cmd) + return; + + // Reset hook? + if (cmd != 0 && cmd < 0x80) + *p = 0; + + part = get_part(chan); + if (part) + part->set_vol(data[2]); +} + +void Player::maybe_set_program(byte *data) { + byte cmd; + byte *p; + uint chan; + Part *part; + + cmd = data[1]; + chan = data[0]; + + // Is this the hook I'm waiting for? + p = &_hook._part_program[chan]; + + if (cmd && *p != cmd) + return; + + if (cmd != 0 && cmd < 0x80) + *p = 0; + + part = get_part(chan); + if (part) + part->set_program(data[2]); +} + +void Player::maybe_set_transpose_part(byte *data) { + byte cmd; + byte *p; + uint chan; + + cmd = data[1]; + chan = data[0]; + + // Is this the hook I'm waiting for? + p = &_hook._part_transpose[chan]; + + if (cmd && *p != cmd) + return; + + // Reset hook? + if (cmd != 0 && cmd < 0x80) + *p = 0; + + part_set_transpose(chan, data[2], (int8)data[3]); +} + +int Player::set_transpose(byte relative, int b) { + Part *part; + + if (b > 24 || b < -24 || relative > 1) + return -1; + if (relative) + b = transpose_clamp(_transpose + b, -7, 7); + + _transpose = b; + + for (part = _parts; part; part = part->_next) { + part->set_transpose(part->_transpose); + } + + return 0; +} + +void Player::clear_active_notes() { + memset(_se->_active_notes, 0, sizeof(_se->_active_notes)); +} + +void Player::clear_active_note(int chan, byte note) { + _se->_active_notes[note] &= ~(1 << chan); +} + +void Player::set_active_note(int chan, byte note) { + _se->_active_notes[note] |= (1 << chan); +} + +void Player::part_set_transpose(uint8 chan, byte relative, int8 b) { + Part *part; + + if (b > 24 || b < -24) + return; + + part = get_part(chan); + if (!part) + return; + if (relative) + b = transpose_clamp(b + part->_transpose, -7, 7); + part->set_transpose(b); +} + +void Player::key_on(uint8 chan, uint8 note, uint8 velocity) { + Part *part; + + part = get_part(chan); + if (!part || !part->_on) + return; + + part->key_on(note, velocity); +} + +void Player::key_off(uint8 chan, uint8 note) { + Part *part; + + for (part = _parts; part; part = part->_next) { + if (part->_chan == (byte)chan && part->_on) + part->key_off(note); + } +} + +bool Player::jump(uint track, uint beat, uint tick) { + byte *mtrk, *cur_mtrk, *scanpos; + uint32 topos, curpos, track_offs; + + if (!_active) + return false; + + mtrk = _se->findTag(_song_index, "MTrk", track); + if (!mtrk) + return false; + + cur_mtrk = _se->findTag(_song_index, "MTrk", _track_index); + if (!cur_mtrk) + return false; + + if (beat == 0) + beat = 1; + + topos = (beat - 1) * _ticks_per_beat + tick; + + if (track == _track_index && topos >= _next_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) + return false; + curpos += get_delta_time(&scanpos); + } + + track_offs = scanpos - mtrk; + + turn_off_pedals(); + + find_sustaining_notes(cur_mtrk + _song_offset, mtrk + track_offs, curpos - topos); + + _beat_index = beat; + _tick_index = tick; + _cur_pos = topos; + _next_pos = curpos; + _timer_counter = 0; + _song_offset = track_offs; + if (track != _track_index) { + _track_index = track; + _loop_counter = 0; + } + _abort = true; + return true; +} + +bool Player::set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick) { + if (tobeat + 1 >= frombeat) + return false; + + if (tobeat == 0) + tobeat = 1; + + _loop_counter = 0; // Because of possible interrupts + _loop_to_beat = tobeat; + _loop_to_tick = totick; + _loop_from_beat = frombeat; + _loop_from_tick = fromtick; + _loop_counter = count; + + return true; +} + +void Player::clear_loop() { + _loop_counter = 0; +} + +void Player::turn_off_pedals() { + Part *part; + + for (part = _parts; part; part = part->_next) { + if (part->_pedal) + part->set_pedal(false); + } +} + +void Player::find_sustaining_notes(byte *a, byte *b, uint32 l) { + uint32 pos; + uint16 mask; + uint16 *bitlist_ptr; + SustainingNotes *sn, *next; + IsNoteCmdData isnote; + int j; + uint num_active; + uint max_off_pos; + + num_active = update_actives(); + + // pos contains number of ticks since current position + pos = _next_pos - _cur_pos; + if ((int32)pos < 0) + pos = 0; + + // Locate the positions where the notes are turned off. + // Remember each note that was turned off. + while (num_active != 0) { + // Is note off? + j = is_note_cmd(&a, &isnote); + if (j == -1) + break; + if (j == 1) { + mask = 1 << isnote.chan; + bitlist_ptr = _se->_active_notes + isnote.note; + if (*bitlist_ptr & mask) { + *bitlist_ptr &= ~mask; + num_active--; + // Get a node from the free list + if ((sn = _se->_sustain_notes_free) == NULL) + return; + _se->_sustain_notes_free = sn->next; + + // Insert it in the beginning of the used list + sn->next = _se->_sustain_notes_used; + _se->_sustain_notes_used = sn; + sn->prev = NULL; + if (sn->next) + sn->next->prev = sn; + + sn->note = isnote.note; + sn->chan = isnote.chan; + sn->player = this; + sn->off_pos = pos; + sn->pos = 0; + sn->counter = 0; + } + } + pos += get_delta_time(&a); + } + + // Find the maximum position where a note was turned off + max_off_pos = 0; + for (sn = _se->_sustain_notes_used; sn; sn = sn->next) { + _se->_active_notes[sn->note] |= (1 << sn->chan); + if (sn->off_pos > max_off_pos) { + max_off_pos = sn->off_pos; + } + } + + // locate positions where notes are turned on + pos = l; + while (pos < max_off_pos) { + j = is_note_cmd(&b, &isnote); + if (j == -1) + break; + if (j == 2) { + mask = 1 << isnote.chan; + bitlist_ptr = _se->_active_notes + isnote.note; + + if (*bitlist_ptr & mask) { + sn = _se->_sustain_notes_used; + while (sn) { + next = sn->next; + if (sn->note == isnote.note && sn->chan == isnote.chan && pos < sn->off_pos) { + *bitlist_ptr &= ~mask; + // Unlink from the sustain list + if (next) + next->prev = sn->prev; + if (sn->prev) + sn->prev->next = next; + else + _se->_sustain_notes_used = next; + // Insert into the free list + sn->next = _se->_sustain_notes_free; + _se->_sustain_notes_free = sn; + } + sn = next; + } + } + } + pos += get_delta_time(&b); + } + + // Concatenate head and used list + if (!_se->_sustain_notes_head) { + _se->_sustain_notes_head = _se->_sustain_notes_used; + _se->_sustain_notes_used = NULL; + return; + } + sn = _se->_sustain_notes_head; + while (sn->next) + sn = sn->next; + sn->next = _se->_sustain_notes_used; + _se->_sustain_notes_used = NULL; + if (sn->next) + sn->next->prev = sn; +} + +Part *Player::get_part(uint8 chan) { + Part *part; + + part = _parts; + while (part) { + if (part->_chan == chan) + return part; + part = part->_next; + } + + part = _se->allocate_part (_priority, _midi); + if (!part) { + warning("no parts available"); + return NULL; + } + + part->_chan = chan; + part->setup(this); + + return part; +} + +uint Player::update_actives() { + Part *part; + uint16 *active; + int count = 0; + + clear_active_notes(); + active = _se->_active_notes; + for (part = _parts; part; part = part->_next) { + if (part->_mc) + count += part->update_actives(active); + } + return count; +} + +void Player::set_priority(int pri) { + Part *part; + + _priority = pri; + for (part = _parts; part; part = part->_next) { + part->set_pri(part->_pri); + } + _se->reallocateMidiChannels (_midi); +} + +void Player::set_pan(int pan) { + Part *part; + + _pan = pan; + for (part = _parts; part; part = part->_next) { + part->set_pan(part->_pan); + } +} + +void Player::set_detune(int detune) { + Part *part; + + _detune = detune; + for (part = _parts; part; part = part->_next) { + part->set_detune(part->_detune); + } +} + +int Player::scan(uint totrack, uint tobeat, uint totick) { + byte *mtrk, *scanptr; + uint32 curpos, topos; + uint32 pos; + + if (!_active) + return -1; + + mtrk = _se->findTag(_song_index, "MTrk", totrack); + if (!mtrk) + return -1; + + if (tobeat == 0) + tobeat++; + + turn_off_parts(); + clear_active_notes(); + scanptr = mtrk; + curpos = get_delta_time(&scanptr); + _scanning = true; + + topos = (tobeat - 1) * _ticks_per_beat + totick; + + while (curpos < topos) { + scanptr = parse_midi(scanptr); + if (!scanptr) { + _scanning = false; + return -1; + } + curpos += get_delta_time(&scanptr); + } + pos = scanptr - mtrk; + + _scanning = false; + _se->reallocateMidiChannels (_midi); + 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; + } + return 0; +} + +void Player::turn_off_parts() { + Part *part; + + for (part = _parts; part; part = part->_next) + part->off(); + _se->reallocateMidiChannels (_midi); +} + +void Player::play_active_notes() { + int i, j; + uint mask; + + for (i = 0; i != 128; i++) { + mask = _se->_active_notes[i]; + for (j = 0; j != 16; j++, mask >>= 1) { + if (mask & 1) { + key_on(j, i, 80); + } + } + } +} + +int Player::set_vol(byte vol) { + Part *part; + + if (vol > 127) + return -1; + + _volume = vol; + _vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7; + + for (part = _parts; part; part = part->_next) { + part->set_vol(part->_vol); + } + + return 0; +} + +int Player::get_param(int param, byte chan) { + switch (param) { + case 0: + return (byte)_priority; + case 1: + return (byte)_volume; + case 2: + return (byte)_pan; + case 3: + return (byte)_transpose; + case 4: + return (byte)_detune; + case 5: + return _speed; + case 6: + return _track_index; + case 7: + return _beat_index; + case 8: + return _tick_index; + case 9: + return _loop_counter; + case 10: + return _loop_to_beat; + case 11: + return _loop_to_tick; + case 12: + return _loop_from_beat; + case 13: + return _loop_from_tick; + case 14: + case 15: + case 16: + case 17: + return query_part_param(param, chan); + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: + return _hook.query_param(param, chan); + default: + return -1; + } +} + +int Player::query_part_param(int param, byte chan) { + Part *part; + + part = _parts; + while (part) { + if (part->_chan == chan) { + switch (param) { + case 14: + return part->_on; + case 15: + return part->_vol; + case 16: + return (int) part->_instrument; + case 17: + return part->_transpose; + default: + return -1; + } + } + part = part->_next; + } + return 129; +} + +void Player::sequencer_timer() { + byte *mtrk; + uint32 counter; + byte *song_ptr; + + // First handle any parameter transitions + // that are occuring. + transitionParameters(); + + // Since the volume parameter can cause + // the player to be deactivated, check + // to make sure we're still active. + if (!_active) + return; + + counter = _timer_counter + _timer_speed; + _timer_counter = counter & 0xFFFF; + _cur_pos += counter >> 16; + _tick_index += counter >> 16; + + if (_tick_index >= _ticks_per_beat) { + _beat_index += _tick_index / _ticks_per_beat; + _tick_index %= _ticks_per_beat; + } + if (_loop_counter && _beat_index >= _loop_from_beat && _tick_index >= _loop_from_tick) { + _loop_counter--; + jump(_track_index, _loop_to_beat, _loop_to_tick); + } + if (_next_pos <= _cur_pos) { + mtrk = _se->findTag(_song_index, "MTrk", _track_index); + if (!mtrk) { + warning("Sound %d was unloaded while active", _song_index); + clear(); + } else { + song_ptr = mtrk + _song_offset; + _abort = false; + + while (_next_pos <= _cur_pos) { + song_ptr = parse_midi(song_ptr); + if (!song_ptr || _abort) + return; + _next_pos += get_delta_time(&song_ptr); + _song_offset = song_ptr - mtrk; + } + } + } +} + +// "time" is referenced as hundredths of a second. +// IS THAT CORRECT?? +// We convert it to microseconds before prceeding +int Player::addParameterFader (int param, int target, int time) { + int start; + + switch (param) { + case ParameterFader::pfVolume: + // Volume fades are handled differently. + start = _volume; + break; + + case ParameterFader::pfTranspose: + // FIXME: Is this transpose? And what's the scale? + // It's set to fade to -2400 in the tunnel of love. + warning ("parameterTransition(3) outside Tunnel of Love?"); + start = _transpose; + target /= 200; + break; + + case ParameterFader::pfSpeed: + // FIXME: Is the speed from 0-100? + // Right now I convert it to 0-128. + start = _speed; + target = target * 128 / 100; + break; + + case 127: + // FIXME: This MIGHT fade ALL supported parameters, + // but I'm not sure. + return 0; + + default: + warning ("Player::addParameterFader(): Unknown parameter %d", param); + return 0; // Should be -1, but we'll let the script think it worked. + } + + ParameterFader *ptr = &_parameterFaders[0]; + ParameterFader *best = 0; + int i; + for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) { + if (ptr->param == param) { + best = ptr; + start = ptr->end; + break; + } else if (!ptr->param) { + best = ptr; + } + } + + if (best) { + best->param = param; + best->start = start; + best->end = target; + best->total_time = (uint32) time * 10000; + best->current_time = 0; + } else { + warning ("IMuse Player %d: Out of parameter faders", _id); + return -1; + } + + return 0; +} + +void Player::transitionParameters() { + uint32 advance = _midi->getBaseTempo() / 500; + int value; + + ParameterFader *ptr = &_parameterFaders[0]; + int i; + for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) { + if (!ptr->param) + continue; + + ptr->current_time += advance; + if (ptr->current_time > ptr->total_time) + ptr->current_time = ptr->total_time; + value = (int32) ptr->start + (int32) (ptr->end - ptr->start) * (int32) ptr->current_time / (int32) ptr->total_time; + + switch (ptr->param) { + case ParameterFader::pfVolume: + // Volume. + if (!value) { + clear(); + return; + } + set_vol ((byte) value); + break; + + case ParameterFader::pfSpeed: + // Speed. + set_speed ((byte) value); + break; + + case ParameterFader::pfTranspose: + // FIXME: Is this really transpose? + set_transpose (0, value); + break; + } + + if (ptr->current_time >= ptr->total_time) + ptr->param = 0; + } +} |