From 102969f8209ba1d75d5d9d51837a0e311eedcf90 Mon Sep 17 00:00:00 2001 From: Gregory Montoir Date: Sun, 5 Nov 2006 00:29:34 +0000 Subject: added basic MIDI playback svn-id: r24617 --- engines/touche/midi.cpp | 157 ++++++++++++++++++++++++++++++++++++++++++++ engines/touche/midi.h | 80 ++++++++++++++++++++++ engines/touche/module.mk | 1 + engines/touche/resource.cpp | 3 +- engines/touche/touche.cpp | 11 ++++ engines/touche/touche.h | 4 ++ 6 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 engines/touche/midi.cpp create mode 100644 engines/touche/midi.h diff --git a/engines/touche/midi.cpp b/engines/touche/midi.cpp new file mode 100644 index 0000000000..cc07897c30 --- /dev/null +++ b/engines/touche/midi.cpp @@ -0,0 +1,157 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "common/mutex.h" +#include "common/stream.h" + +#include "sound/midiparser.h" + +#include "touche/midi.h" + +namespace Touche { + +MidiPlayer::MidiPlayer(MidiDriver *driver) + : _masterVolume(255), _isPlaying(false), _driver(driver), _parser(0), _midiData(0) { + assert(_driver); + memset(_channelsTable, 0, sizeof(_channelsTable)); + memset(_channelsVolume, 0, sizeof(_channelsVolume)); + open(); +} + +MidiPlayer::~MidiPlayer() { + close(); +} + +void MidiPlayer::play(Common::ReadStream &stream, int size, bool loop) { + stop(); + _midiData = (uint8 *)malloc(size); + if (_midiData) { + stream.read(_midiData, size); + _mutex.lock(); + _parser->loadMusic(_midiData, size); + _parser->setTrack(0); + _isLooping = loop; + _isPlaying = true; + _mutex.unlock(); + } +} + +void MidiPlayer::stop() { + _mutex.lock(); + if (_isPlaying) { + _isPlaying = false; + _parser->unloadMusic(); + free(_midiData); + _midiData = 0; + } + _mutex.unlock(); +} + +void MidiPlayer::updateTimer() { + _mutex.lock(); + if (_isPlaying) { + _parser->onTimer(); + } + _mutex.unlock(); +} + +void MidiPlayer::setVolume(int volume) { + if (volume < 0) { + volume = 0; + } else if (volume > 255) { + volume = 255; + } + for (int i = 0; i < NUM_CHANNELS; ++i) { + if (_channelsTable[i]) { + _channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255); + } + } +} + +int MidiPlayer::open() { + int ret = _driver->open(); + if (ret == 0) { + _parser = MidiParser::createParser_SMF(); + _parser->setMidiDriver(this); + _parser->setTimerRate(_driver->getBaseTempo()); + _driver->setTimerCallback(this, &timerCallback); + } + return ret; +} + +void MidiPlayer::close() { + _mutex.lock(); + _driver->setTimerCallback(NULL, NULL); + _mutex.unlock(); + stop(); + _parser->setMidiDriver(NULL); + delete _parser; + _driver->close(); + _driver = 0; +} + +void MidiPlayer::send(uint32 b) { + byte volume, ch = (byte)(b & 0xF); + switch (b & 0xFFF0) { + case 0x07B0: // volume change + volume = (byte)((b >> 16) & 0x7F); + _channelsVolume[ch] = volume; + volume = volume * _masterVolume / 255; + b = (b & 0xFF00FFFF) | (volume << 16); + break; + case 0x7BB0: // all notes off + if (!_channelsTable[ch]) { + // channel not yet allocated, no need to send the event + return; + } + break; + } + if (!_channelsTable[ch]) { + _channelsTable[ch] = (ch == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + } + if (_channelsTable[ch]) { + _channelsTable[ch]->send(b); + } +} + +void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) { + switch (type) { + case 0x2F: // end of Track + if (_isLooping) { + _parser->jumpToTick(0); + } else { + stop(); + } + break; + default: +// warning("Unhandled meta event: %02x", type); + break; + } +} + +void MidiPlayer::timerCallback(void *p) { + MidiPlayer *player = (MidiPlayer *)p; + player->updateTimer(); +} + +} // Touche namespace diff --git a/engines/touche/midi.h b/engines/touche/midi.h new file mode 100644 index 0000000000..e012761d70 --- /dev/null +++ b/engines/touche/midi.h @@ -0,0 +1,80 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef TOUCHE_MIDI_H +#define TOUCHE_MIDI_H + +#include "common/util.h" +#include "sound/mididrv.h" + +class MidiParser; + +namespace Common { + class ReadStream; + class Mutex; +} + +namespace Touche { + +class MidiPlayer : public MidiDriver { +public: + + enum { + NUM_CHANNELS = 16 + }; + + MidiPlayer(MidiDriver *driver); + ~MidiPlayer(); + + void play(Common::ReadStream &stream, int size, bool loop = false); + void stop(); + void updateTimer(); + void setVolume(int volume); + + // MidiDriver interface + int open(); + void close(); + void send(uint32 b); + void metaEvent(byte type, byte *data, uint16 length); + void setTimerCallback(void *timerParam, void (*timerProc)(void *)) { } + uint32 getBaseTempo() { return _driver ? _driver->getBaseTempo() : 0; } + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } + +private: + + static void timerCallback(void *p); + + int _masterVolume; + bool _isLooping; + bool _isPlaying; + MidiDriver *_driver; + MidiParser *_parser; + MidiChannel *_channelsTable[NUM_CHANNELS]; + byte _channelsVolume[NUM_CHANNELS]; + uint8 *_midiData; + Common::Mutex _mutex; +}; + +} // namespace Touche + +#endif diff --git a/engines/touche/module.mk b/engines/touche/module.mk index c2d12ca813..1c68d1b7c3 100644 --- a/engines/touche/module.mk +++ b/engines/touche/module.mk @@ -2,6 +2,7 @@ MODULE := engines/touche MODULE_OBJS := \ graphics.o \ + midi.o \ plugin.o \ opcodes.o \ resource.o \ diff --git a/engines/touche/resource.cpp b/engines/touche/resource.cpp index ef7b1612c4..6a5dc02428 100644 --- a/engines/touche/resource.cpp +++ b/engines/touche/resource.cpp @@ -28,6 +28,7 @@ #include "sound/voc.h" #include "sound/vorbis.h" +#include "touche/midi.h" #include "touche/touche.h" #include "touche/graphics.h" @@ -564,7 +565,7 @@ void ToucheEngine::res_loadMusic(int num) { uint32 size; const uint32 offs = res_getDataOffset(kResourceTypeMusic, num, &size); _fData.seek(offs); - // XXX start MIDI data playback + _midiPlayer->play(_fData, size); } void ToucheEngine::res_loadSpeech(int num) { diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp index 9d564bca6d..2c1981ade7 100644 --- a/engines/touche/touche.cpp +++ b/engines/touche/touche.cpp @@ -24,6 +24,9 @@ #include "common/config-manager.h" #include "common/system.h" +#include "sound/mididrv.h" + +#include "touche/midi.h" #include "touche/touche.h" #include "touche/graphics.h" @@ -74,10 +77,15 @@ ToucheEngine::ToucheEngine(OSystem *system, Common::Language language) Common::addSpecialDebugLevel(kDebugResource, "Resource", "Resource debug level"); Common::addSpecialDebugLevel(kDebugOpcodes, "Opcodes", "Opcodes debug level"); Common::addSpecialDebugLevel(kDebugUserIntf, "UserIntf", "UserInterface debug level"); + + int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI); + MidiDriver *driver = MidiDriver::createMidi(midiDriver); + _midiPlayer = new MidiPlayer(driver); } ToucheEngine::~ToucheEngine() { Common::clearAllSpecialDebugLevels(); + delete _midiPlayer; } int ToucheEngine::init() { @@ -88,6 +96,9 @@ int ToucheEngine::init() { _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, Audio::Mixer::kMaxMixerVolume); + + _midiPlayer->setVolume(ConfMan.getInt("music_volume")); return 0; } diff --git a/engines/touche/touche.h b/engines/touche/touche.h index 61f38b52ab..4c900afc35 100644 --- a/engines/touche/touche.h +++ b/engines/touche/touche.h @@ -299,6 +299,8 @@ enum SaveLoadMode { kLoadGameState }; +class MidiPlayer; + class ToucheEngine: public Engine { public: @@ -595,6 +597,8 @@ protected: void ui_displayTextMode(int str); + MidiPlayer *_midiPlayer; + Common::Language _language; Common::RandomSource _rnd; -- cgit v1.2.3