mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-21 01:08:25 +00:00
1378 lines
28 KiB
C++
1378 lines
28 KiB
C++
|
/* 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;
|
||
|
}
|
||
|
}
|