mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
(fix bug #14618: Inaccurate fades in INDY4, "Test 1") This part of the bug ticket is not about fades. The reason for the glitch is that the driver has no unused channels left and the newer allocation mechanism is not able to free up some of the used channels here.
483 lines
11 KiB
C++
483 lines
11 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "common/debug.h"
|
|
#include "common/textconsole.h"
|
|
#include "common/util.h"
|
|
#include "scumm/scumm.h"
|
|
#include "scumm/imuse/imuse_internal.h"
|
|
|
|
namespace Scumm {
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
// IMuse Part implementation
|
|
//
|
|
////////////////////////////////////////
|
|
|
|
Part::Part() {
|
|
_slot = 0;
|
|
_next = nullptr;
|
|
_prev = nullptr;
|
|
_mc = nullptr;
|
|
_player = nullptr;
|
|
_pitchbend = 0;
|
|
_pitchbend_factor = 0;
|
|
_volControlSensitivity = 127;
|
|
_transpose = 0;
|
|
_transpose_eff = 0;
|
|
_vol = 0;
|
|
_vol_eff = 0;
|
|
_detune = 0;
|
|
_detune_eff = 0;
|
|
_pan = 0;
|
|
_pan_eff = 0;
|
|
_polyphony = 0;
|
|
_on = false;
|
|
_modwheel = 0;
|
|
_pedal = false;
|
|
_pri = 0;
|
|
_pri_eff = 0;
|
|
_chan = 0;
|
|
_effect_level = 0;
|
|
_chorus = 0;
|
|
_percussion = 0;
|
|
_bank = 0;
|
|
_unassigned_instrument = false;
|
|
_se = nullptr;
|
|
}
|
|
|
|
void Part::saveLoadWithSerializer(Common::Serializer &ser) {
|
|
int num;
|
|
if (ser.isSaving()) {
|
|
num = (_next ? (_next - _se->_parts + 1) : 0);
|
|
ser.syncAsUint16LE(num);
|
|
|
|
num = (_prev ? (_prev - _se->_parts + 1) : 0);
|
|
ser.syncAsUint16LE(num);
|
|
|
|
num = (_player ? (_player - _se->_players + 1) : 0);
|
|
ser.syncAsUint16LE(num);
|
|
} else {
|
|
ser.syncAsUint16LE(num);
|
|
_next = (num ? &_se->_parts[num - 1] : nullptr);
|
|
|
|
ser.syncAsUint16LE(num);
|
|
_prev = (num ? &_se->_parts[num - 1] : nullptr);
|
|
|
|
ser.syncAsUint16LE(num);
|
|
_player = (num ? &_se->_players[num - 1] : nullptr);
|
|
}
|
|
|
|
ser.syncAsSint16LE(_pitchbend, VER(8));
|
|
ser.syncAsByte(_pitchbend_factor, VER(8));
|
|
ser.syncAsSByte(_transpose, VER(8));
|
|
ser.syncAsByte(_vol, VER(8));
|
|
ser.syncAsSByte(_detune, VER(8));
|
|
ser.syncAsSByte(_pan, VER(8));
|
|
ser.syncAsByte(_on, VER(8));
|
|
ser.syncAsByte(_modwheel, VER(8));
|
|
ser.syncAsByte(_pedal, VER(8));
|
|
ser.skip(1, VER(8), VER(16)); // _program
|
|
ser.syncAsByte(_pri, VER(8));
|
|
ser.syncAsByte(_chan, VER(8));
|
|
ser.syncAsByte(_effect_level, VER(8));
|
|
ser.syncAsByte(_chorus, VER(8));
|
|
ser.syncAsByte(_percussion, VER(8));
|
|
ser.syncAsByte(_bank, VER(8));
|
|
ser.syncAsByte(_polyphony, VER(116));
|
|
ser.syncAsByte(_volControlSensitivity, VER(116));
|
|
}
|
|
|
|
void Part::set_detune(int8 detune) {
|
|
// Sam&Max does not have detune except for the parameter faders, so the argument
|
|
// here will always be 0 and the only relevant part will be the detune from the player.
|
|
_detune_eff = _se->_newSystem ? _player->getDetune() : clamp((_detune = detune) + _player->getDetune(), -128, 127);
|
|
sendDetune();
|
|
}
|
|
|
|
void Part::pitchBend(int16 value) {
|
|
_pitchbend = value;
|
|
sendPitchBend();
|
|
}
|
|
|
|
void Part::volume(byte value) {
|
|
_vol = value;
|
|
sendVolume(0);
|
|
}
|
|
|
|
void Part::volControlSensitivity(byte value) {
|
|
if (value > 127)
|
|
return;
|
|
_volControlSensitivity = value;
|
|
sendVolume(0);
|
|
}
|
|
|
|
void Part::set_pri(int8 pri) {
|
|
_pri_eff = clamp((_pri = pri) + _player->getPriority(), 0, 255);
|
|
if (_mc)
|
|
_mc->priority(_pri_eff);
|
|
}
|
|
|
|
void Part::set_pan(int8 pan) {
|
|
_pan_eff = clamp((_pan = pan) + _player->getPan(), -64, 63);
|
|
sendPanPosition(_pan_eff + 0x40);
|
|
}
|
|
|
|
void Part::set_polyphony(byte val) {
|
|
if (!_se->_newSystem)
|
|
return;
|
|
_polyphony = val;
|
|
if (_mc)
|
|
_mc->controlChange(17, val);
|
|
}
|
|
|
|
void Part::set_transpose(int8 transpose, int8 clipRangeLow, int8 clipRangeHi) {
|
|
if (_se->_game_id == GID_TENTACLE && (transpose > 24 || transpose < -24))
|
|
return;
|
|
|
|
_transpose = transpose;
|
|
// The Amiga versions have a signed/unsigned bug which makes the check for _transpose == -128 impossible. They actually check for
|
|
// a value of 128 with a signed int8 (a signed int8 can never be 128). The playback depends on this being implemented exactly
|
|
// like in the original driver. I found this bug with the WinUAE debugger. The DOS versions do not have that bug.
|
|
_transpose_eff = (_se->_soundType != MDT_AMIGA && _transpose == -128) ? 0 : transpose_clamp(_transpose + _player->getTranspose(), clipRangeLow, clipRangeHi);
|
|
sendTranspose();
|
|
}
|
|
|
|
void Part::sustain(bool value) {
|
|
_pedal = value;
|
|
if (_mc)
|
|
_mc->sustain(value);
|
|
}
|
|
|
|
void Part::modulationWheel(byte value) {
|
|
_modwheel = value;
|
|
if (_mc)
|
|
_mc->modulationWheel(value);
|
|
}
|
|
|
|
void Part::chorusLevel(byte value) {
|
|
_chorus = value;
|
|
if (_mc)
|
|
_mc->chorusLevel(value);
|
|
}
|
|
|
|
void Part::effectLevel(byte value) {
|
|
_effect_level = value;
|
|
sendEffectLevel(value);
|
|
}
|
|
|
|
void Part::fix_after_load() {
|
|
int lim = (_se->_game_id == GID_TENTACLE || _se->_soundType == MDT_AMIGA|| _se->isNativeMT32()) ? 12 : 24;
|
|
set_transpose(_transpose, -lim, lim);
|
|
volume(_vol);
|
|
set_detune(_detune);
|
|
set_pri(_pri);
|
|
set_pan(_pan);
|
|
|
|
if (!_se->_dynamicChanAllocation && !_mc) {
|
|
_mc = _se->allocateChannel(_player->getMidiDriver(), _pri_eff);
|
|
if (!_mc)
|
|
_se->suspendPart(this);
|
|
}
|
|
|
|
sendAll();
|
|
}
|
|
|
|
void Part::pitchBendFactor(byte value) {
|
|
if (value > 12)
|
|
return;
|
|
pitchBend(0);
|
|
_pitchbend_factor = value;
|
|
if (_mc)
|
|
_mc->pitchBendFactor(value);
|
|
}
|
|
|
|
void Part::set_onoff(bool on) {
|
|
if (_on != on) {
|
|
_on = on;
|
|
if (!on)
|
|
off();
|
|
if (!_percussion)
|
|
_player->_se->reallocateMidiChannels(_player->getMidiDriver());
|
|
}
|
|
}
|
|
|
|
void Part::set_instrument(byte *data) {
|
|
if (_se->_soundType == MDT_PCSPK)
|
|
_instrument.pcspk(data);
|
|
else
|
|
_instrument.adlib(data);
|
|
|
|
if (clearToTransmit())
|
|
_instrument.send(_mc);
|
|
}
|
|
|
|
void Part::load_global_instrument(byte slot) {
|
|
_player->_se->copyGlobalInstrument(slot, &_instrument);
|
|
if (clearToTransmit())
|
|
_instrument.send(_mc);
|
|
}
|
|
|
|
void Part::noteOn(byte note, byte velocity) {
|
|
if (!_on)
|
|
return;
|
|
|
|
MidiChannel *mc = _mc;
|
|
|
|
// DEBUG
|
|
if (_unassigned_instrument && !_percussion) {
|
|
_unassigned_instrument = false;
|
|
if (!_instrument.isValid()) {
|
|
debug(0, "[%02d] No instrument specified", (int)_chan);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (mc && _instrument.isValid()) {
|
|
mc->noteOn(note, velocity);
|
|
} else if (_percussion) {
|
|
mc = _player->getMidiDriver()->getPercussionChannel();
|
|
if (!mc)
|
|
return;
|
|
|
|
if (_vol_eff != _se->_rhyState.vol)
|
|
mc->volume(_vol_eff);
|
|
|
|
if (_se->_newSystem) {
|
|
if (_pri_eff != _se->_rhyState.prio)
|
|
mc->priority(_pri_eff);
|
|
if (_polyphony != _se->_rhyState.poly)
|
|
mc->controlChange(17, _polyphony);
|
|
|
|
} else if ((note < 35) && (!_player->_se->isNativeMT32())) {
|
|
note = Instrument::_gmRhythmMap[note];
|
|
}
|
|
|
|
_se->_rhyState = IMuseInternal::RhyState(_vol_eff, _polyphony, _pri_eff);
|
|
|
|
mc->noteOn(note, velocity);
|
|
}
|
|
}
|
|
|
|
void Part::noteOff(byte note) {
|
|
if (!_on)
|
|
return;
|
|
|
|
MidiChannel *mc = _mc;
|
|
if (mc) {
|
|
mc->noteOff(note);
|
|
} else if (_percussion) {
|
|
mc = _player->getMidiDriver()->getPercussionChannel();
|
|
if (mc)
|
|
mc->noteOff(note);
|
|
}
|
|
}
|
|
|
|
void Part::init(bool useNativeMT32) {
|
|
_player = nullptr;
|
|
_next = nullptr;
|
|
_prev = nullptr;
|
|
_mc = nullptr;
|
|
_instrument.setNativeMT32Mode(useNativeMT32);
|
|
}
|
|
|
|
void Part::setup(Player *player) {
|
|
_player = player;
|
|
|
|
_percussion = (player->isMIDI() && _chan == 9); // true;
|
|
_on = true;
|
|
_pri_eff = player->getPriority();
|
|
_pri = 0;
|
|
_vol = 127;
|
|
_vol_eff = player->getEffectiveVolume();
|
|
_pan = clamp(player->getPan(), -64, 63);
|
|
_transpose_eff = player->getTranspose();
|
|
_transpose = 0;
|
|
_detune = 0;
|
|
_detune_eff = player->getDetune();
|
|
_pitchbend_factor = 2;
|
|
_volControlSensitivity = 127;
|
|
_polyphony = 1;
|
|
_pitchbend = 0;
|
|
_effect_level = player->_se->isNativeMT32() ? 127 : 64;
|
|
_instrument.clear();
|
|
_unassigned_instrument = true;
|
|
_chorus = 0;
|
|
_modwheel = 0;
|
|
_bank = 0;
|
|
_pedal = false;
|
|
_mc = nullptr;
|
|
}
|
|
|
|
void Part::uninit() {
|
|
if (!_player)
|
|
return;
|
|
off();
|
|
_player->removePart(this);
|
|
_se->removeSuspendedPart(this);
|
|
_player = nullptr;
|
|
}
|
|
|
|
void Part::off() {
|
|
if (_mc) {
|
|
_mc->allNotesOff();
|
|
if (!_se->reassignChannelAndResumePart(_mc))
|
|
_mc->release();
|
|
_mc = nullptr;
|
|
}
|
|
}
|
|
|
|
bool Part::clearToTransmit() {
|
|
if (_mc)
|
|
return true;
|
|
if (_instrument.isValid())
|
|
_player->_se->reallocateMidiChannels(_player->getMidiDriver());
|
|
return false;
|
|
}
|
|
|
|
void Part::sendAll() {
|
|
if (!clearToTransmit())
|
|
return;
|
|
|
|
_mc->pitchBendFactor(_pitchbend_factor);
|
|
sendTranspose();
|
|
sendDetune();
|
|
sendPitchBend();
|
|
_mc->volume(_vol_eff);
|
|
_mc->sustain(_pedal);
|
|
_mc->modulationWheel(_modwheel);
|
|
sendPanPosition(_pan_eff + 0x40);
|
|
sendPolyphony();
|
|
|
|
if (_instrument.isValid())
|
|
_instrument.send(_mc);
|
|
|
|
// We need to send the effect level after setting up the instrument
|
|
// otherwise the reverb setting for MT-32 will be overwritten.
|
|
sendEffectLevel(_effect_level);
|
|
|
|
_mc->chorusLevel(_chorus);
|
|
_mc->priority(_pri_eff);
|
|
}
|
|
|
|
void Part::sendPitchBend() {
|
|
if (_se->_newSystem && !_pitchbend_factor) {
|
|
sendVolumeFade();
|
|
return;
|
|
}
|
|
|
|
if (_mc)
|
|
_mc->pitchBend(_pitchbend);
|
|
}
|
|
|
|
void Part::sendVolume(int8 fadeModifier) {
|
|
uint16 vol = (_vol + fadeModifier + 1) * _player->getEffectiveVolume();
|
|
|
|
if (_se->_newSystem)
|
|
vol = (vol * (_volControlSensitivity + 1)) >> 7;
|
|
|
|
_vol_eff = vol >> 7;
|
|
|
|
if (_mc)
|
|
_mc->volume(_vol_eff);
|
|
}
|
|
|
|
void Part::sendVolumeFade() {
|
|
int16 fadeModifier = ((((_pitchbend >= 0) ? 127 - _vol : _vol) + 1) * _pitchbend) >> 7;
|
|
sendVolume(fadeModifier);
|
|
}
|
|
|
|
void Part::sendTranspose() {
|
|
if (!_mc)
|
|
return;
|
|
|
|
_mc->transpose(_transpose_eff);
|
|
}
|
|
|
|
void Part::sendDetune() {
|
|
if (!_mc)
|
|
return;
|
|
|
|
_mc->detune(_detune_eff);
|
|
}
|
|
|
|
void Part::programChange(byte value) {
|
|
_bank = 0;
|
|
_instrument.program(value, _player->isMT32());
|
|
if (clearToTransmit())
|
|
_instrument.send(_mc);
|
|
}
|
|
|
|
void Part::set_instrument(uint b) {
|
|
_bank = (byte)(b >> 8);
|
|
if (_bank)
|
|
error("Non-zero instrument bank selection. Please report this");
|
|
// HACK: Horrible hack to allow tracing of program change source.
|
|
// The Mac m68k versions of MI2 and Indy4 use a different program "bank"
|
|
// when it gets program change events through the iMuse SysEx handler.
|
|
// We emulate this by introducing a special instrument, which sets
|
|
// the instrument via sysEx_customInstrument. This seems to be
|
|
// exclusively used for special sound effects like the "spit" sound.
|
|
if (g_scumm->isMacM68kIMuse()) {
|
|
_instrument.macSfx(b);
|
|
} else {
|
|
_instrument.program((byte)b, _player->isMT32());
|
|
}
|
|
if (clearToTransmit())
|
|
_instrument.send(_mc);
|
|
}
|
|
|
|
void Part::allNotesOff() {
|
|
if (!_mc)
|
|
return;
|
|
_mc->allNotesOff();
|
|
}
|
|
|
|
void Part::sendPanPosition(uint8 value) {
|
|
if (!_mc)
|
|
return;
|
|
|
|
// As described in bug report #1849 "MI2: Minor problems in native MT-32 mode"
|
|
// the original iMuse MT-32 driver did revert the panning. So we do the same
|
|
// here in our code to have correctly panned sound output.
|
|
if (_player->_se->isNativeMT32())
|
|
value = 127 - value;
|
|
|
|
_mc->panPosition(value);
|
|
}
|
|
|
|
void Part::sendEffectLevel(uint8 value) {
|
|
if (!_mc)
|
|
return;
|
|
_mc->effectLevel(value);
|
|
}
|
|
|
|
void Part::sendPolyphony() {
|
|
if (!_mc || !_se->_newSystem)
|
|
return;
|
|
_mc->controlChange(17, _polyphony);
|
|
}
|
|
|
|
} // End of namespace Scumm
|