Logo Search packages:      
Sourcecode: scummvm version File versions

midiplayer.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$
 * $Id$
 *
 */

#include "audio/midiplayer.h"
#include "audio/midiparser.h"

#include "common/config-manager.h"

namespace Audio {

MidiPlayer::MidiPlayer() :
      _driver(0),
      _parser(0),
      _midiData(0),
      _isLooping(false),
      _isPlaying(false),
      _masterVolume(0),
      _nativeMT32(false) {

      memset(_channelsTable, 0, sizeof(_channelsTable));
      memset(_channelsVolume, 127, sizeof(_channelsVolume));

// TODO
}

MidiPlayer::~MidiPlayer() {
      // FIXME/TODO: In some engines, stop() was called first;
      // in others, _driver->setTimerCallback(NULL, NULL) came first.
      // Hopefully, this make no real difference, but we should
      // watch out for regressions.
      stop();

      // Unhook & unload the driver
      if (_driver) {
            _driver->setTimerCallback(0, 0);
            _driver->close();
            delete _driver;
            _driver = 0;
      }
}

void MidiPlayer::createDriver(int flags) {
      MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(flags);
      _nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));

      _driver = MidiDriver::createMidi(dev);
      assert(_driver);
      if (_nativeMT32)
            _driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
}


00075 void MidiPlayer::setVolume(int volume) {
      volume = CLIP(volume, 0, 255);
      if (_masterVolume == volume)
            return;

      Common::StackLock lock(_mutex);

      _masterVolume = volume;
      for (int i = 0; i < kNumChannels; ++i) {
            if (_channelsTable[i]) {
                  _channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255);
            }
      }
}

00090 void MidiPlayer::syncVolume() {
      int volume = ConfMan.getInt("music_volume");
      if (ConfMan.getBool("mute")) {
            volume = -1;
      }
      setVolume(volume);
}


00099 void MidiPlayer::send(uint32 b) {
      byte ch = (byte)(b & 0x0F);
      if ((b & 0xFFF0) == 0x07B0) {
            // Adjust volume changes by master volume
            byte volume = (byte)((b >> 16) & 0x7F);
            _channelsVolume[ch] = volume;
            volume = volume * _masterVolume / 255;
            b = (b & 0xFF00FFFF) | (volume << 16);
      } else if ((b & 0xFFF0) == 0x007BB0) {
            // Only respond to All Notes Off if this channel
            // has currently been allocated
            if (!_channelsTable[ch])
                  return;
      }

      sendToChannel(ch, b);
}

00117 void MidiPlayer::sendToChannel(byte ch, uint32 b) {
      if (!_channelsTable[ch]) {
            _channelsTable[ch] = (ch == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
            // TODO: Some engines overload this method to insert code at this
            // point which calls the channel's volume() method.
            // Does this make sense, and should we maybe do it in general?
      }
      if (_channelsTable[ch]) {
            _channelsTable[ch]->send(b);
      }
}

void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) {
      switch (type) {
      case 0x2F:  // End of Track
            endOfTrack();
            break;
      default:
            //warning("Unhandled meta event: %02x", type);
            break;
      }
}

00140 void MidiPlayer::endOfTrack() {
      if (_isLooping) {
            assert(_parser);
            _parser->jumpToTick(0);
      } else
            stop();
}

void MidiPlayer::timerCallback(void *data) {
      assert(data);
      ((MidiPlayer *)data)->onTimer();
}

void MidiPlayer::onTimer() {
      Common::StackLock lock(_mutex);

      // TODO: Maybe we can replace _isPlaying
      // by a simple check for "_parser != 0" ?

      if (_isPlaying && _parser) {
            _parser->onTimer();
      }
}


void MidiPlayer::stop() {
      Common::StackLock lock(_mutex);

      _isPlaying = false;
      if (_parser) {
            _parser->unloadMusic();

            // FIXME/TODO: The MidiParser destructor calls allNotesOff()
            // but unloadMusic also does. To suppress double notes-off,
            // we reset the midi driver of _parser before deleting it.
            // This smells very fishy, in any case.
            _parser->setMidiDriver(0);

            delete _parser;
            _parser = NULL;
      }

      free(_midiData);
      _midiData = 0;
}

void MidiPlayer::pause() {
//    debugC(2, kDraciSoundDebugLevel, "Pausing track %d", _track);
      _isPlaying = false;
      setVolume(-1);    // FIXME: This should be 0, shouldn't it?
}

void MidiPlayer::resume() {
//    debugC(2, kDraciSoundDebugLevel, "Resuming track %d", _track);
      syncVolume();
      _isPlaying = true;
}

} // End of namespace Audio

Generated by  Doxygen 1.6.0   Back to index