Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

imuse_part.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/engines/scumm/imuse/imuse_part.cpp $
 * $Id: imuse_part.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */



#include "common/util.h"
#include "scumm/imuse/imuse_internal.h"
#include "scumm/saveload.h"

namespace Scumm {

////////////////////////////////////////
//
//  IMuse Part implementation
//
////////////////////////////////////////

Part::Part() {
      _slot = 0;
      _next = 0;
      _prev = 0;
      _mc = 0;
      _player = 0;
      _pitchbend = 0;
      _pitchbend_factor = 0;
      _transpose = 0;
      _transpose_eff = 0;
      _vol = 0;
      _vol_eff = 0;
      _detune = 0;
      _detune_eff = 0;
      _pan = 0;
      _pan_eff = 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;
}

void Part::saveLoadWithSerializer(Serializer *ser) {
      const SaveLoadEntry partEntries[] = {
            MKLINE(Part, _pitchbend, sleInt16, VER(8)),
            MKLINE(Part, _pitchbend_factor, sleUint8, VER(8)),
            MKLINE(Part, _transpose, sleInt8, VER(8)),
            MKLINE(Part, _vol, sleUint8, VER(8)),
            MKLINE(Part, _detune, sleInt8, VER(8)),
            MKLINE(Part, _pan, sleInt8, VER(8)),
            MKLINE(Part, _on, sleUint8, VER(8)),
            MKLINE(Part, _modwheel, sleUint8, VER(8)),
            MKLINE(Part, _pedal, sleUint8, VER(8)),
            MK_OBSOLETE(Part, _program, sleUint8, VER(8), VER(16)),
            MKLINE(Part, _pri, sleUint8, VER(8)),
            MKLINE(Part, _chan, sleUint8, VER(8)),
            MKLINE(Part, _effect_level, sleUint8, VER(8)),
            MKLINE(Part, _chorus, sleUint8, VER(8)),
            MKLINE(Part, _percussion, sleUint8, VER(8)),
            MKLINE(Part, _bank, sleUint8, VER(8)),
            MKEND()
      };

      int num;
      if (ser->isSaving()) {
            num = (_next ? (_next - _se->_parts + 1) : 0);
            ser->saveUint16(num);

            num = (_prev ? (_prev - _se->_parts + 1) : 0);
            ser->saveUint16(num);

            num = (_player ? (_player - _se->_players + 1) : 0);
            ser->saveUint16(num);
      } else {
            num = ser->loadUint16();
            _next = (num ? &_se->_parts[num - 1] : 0);

            num = ser->loadUint16();
            _prev = (num ? &_se->_parts[num - 1] : 0);

            num = ser->loadUint16();
            _player = (num ? &_se->_players[num - 1] : 0);
      }
      ser->saveLoadEntries(this, partEntries);
}

void Part::set_detune(int8 detune) {
      _detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127);
      if (_mc)
            sendPitchBend();
}

void Part::pitchBend(int16 value) {
      _pitchbend = value;
      if (_mc)
            sendPitchBend();
}

void Part::volume(byte value) {
      _vol_eff = ((_vol = value) + 1) * _player->getEffectiveVolume() >> 7;
      if (_mc)
            _mc->volume(_vol_eff);
}

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);
      if (_mc)
            _mc->panPosition(_pan_eff + 0x40);
}

void Part::set_transpose(int8 transpose) {
      _transpose_eff = transpose_clamp((_transpose = transpose) + _player->getTranspose(), -24, 24);
      if (_mc)
            sendPitchBend();
}

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;
      if (_mc)
            _mc->effectLevel(value);
}

void Part::fix_after_load() {
      set_transpose(_transpose);
      volume(_vol);
      set_detune(_detune);
      set_pri(_pri);
      set_pan(_pan);
      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) {
      _instrument.adlib(data);
      if (clearToTransmit())
            _instrument.send(_mc);
}

void Part::load_global_instrument(byte slot) {
      _player->_se->copyGlobalAdlibInstrument(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;

            // FIXME: The following is evil, EVIL!!! Either prev_vol_eff is
            // actually meant to be a member of the Part class (i.e. each
            // instance of Part keeps a separate copy of it); or it really
            // is supposed to be shared by all Part instances -- but then it
            // should be implemented as a class static var. As it is, using
            // a function level static var in most cases is arcane and evil.
            static byte prev_vol_eff = 128;
            if (_vol_eff != prev_vol_eff){
                  mc->volume(_vol_eff);
                  prev_vol_eff = _vol_eff;
            }
            if ((note < 35) && (!_player->_se->isNativeMT32()))
                  note = Instrument::_gmRhythmMap[note];

            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() {
      _player = NULL;
      _next = NULL;
      _prev = NULL;
      _mc = NULL;
}

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;
      _pitchbend = 0;
      _effect_level = 64;
      _instrument.clear();
      _unassigned_instrument = true;
      _chorus = 0;
      _modwheel = 0;
      _bank = 0;
      _pedal = false;
      _mc = NULL;
}

void Part::uninit() {
      if (!_player)
            return;
      off();
      _player->removePart(this);
      _player = NULL;
}

void Part::off() {
      if (_mc) {
            _mc->allNotesOff();
            _mc->release();
            _mc = NULL;
      }
}

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);
      sendPitchBend();
      _mc->volume(_vol_eff);
      _mc->sustain(_pedal);
      _mc->modulationWheel(_modwheel);
      _mc->panPosition(_pan_eff + 0x40);
      _mc->effectLevel(_effect_level);
      if (_instrument.isValid())
            _instrument.send(_mc);
      _mc->chorusLevel(_chorus);
      _mc->priority(_pri_eff);
}

void Part::sendPitchBend() {
      int16 bend = _pitchbend;
      // RPN-based pitchbend range doesn't work for the MT32,
      // so we'll do the scaling ourselves.
      if (_player->_se->isNativeMT32())
            bend = bend * _pitchbend_factor / 12;
      _mc->pitchBend(clamp(bend + (_detune_eff * 64 / 12) + (_transpose_eff * 8192 / 12), -8192, 8191));
}

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");
      _instrument.program((byte)b, _player->isMT32());
      if (clearToTransmit())
            _instrument.send(_mc);
}

void Part::allNotesOff() {
      if (!_mc)
            return;
      _mc->allNotesOff();
}

} // End of namespace Scumm

Generated by  Doxygen 1.6.0   Back to index