Added (early) midi player code.

Added "screen flash" effect.
Implemented opcodes:
- o1_PLAYMUS
- o1_STOPMUS
- o1_ISMUS
- o1_FLASH
- o1_LOADSND
- o1_LOADMUS

svn-id: r31682
This commit is contained in:
Benjamin Haisch 2008-04-23 23:22:02 +00:00
parent 6562a7cd8a
commit efeb8a3193
10 changed files with 525 additions and 4 deletions

View File

@ -44,6 +44,7 @@
#include "made/screen.h"
#include "made/script.h"
#include "made/sound.h"
#include "made/music.h"
#include "made/redreader.h"
namespace Made {
@ -88,6 +89,24 @@ MadeEngine::MadeEngine(OSystem *syst, const MadeGameDescription *gameDesc) : Eng
_dat = new GameDatabase();
_script = new ScriptInterpreter(this);
int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
bool adlib = (midiDriver == MD_ADLIB);
MidiDriver *driver = MidiDriver::createMidi(midiDriver);
if (native_mt32)
driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
_music = new Music(driver, _musicVolume);
_music->setNativeMT32(native_mt32);
_music->setAdlib(adlib);
_musicVolume = ConfMan.getInt("music_volume");
if (!_musicVolume) {
debug(1, "Music disabled.");
}
}
MadeEngine::~MadeEngine() {
@ -97,6 +116,7 @@ MadeEngine::~MadeEngine() {
delete _screen;
delete _dat;
delete _script;
delete _music;
}
int MadeEngine::init() {

View File

@ -61,6 +61,7 @@ class PmvPlayer;
class Screen;
class ScriptInterpreter;
class GameDatabase;
class Music;
class MadeEngine : public ::Engine {
int _gameId;
@ -93,10 +94,12 @@ public:
Screen *_screen;
GameDatabase *_dat;
ScriptInterpreter *_script;
Music *_music;
int _eventMouseX, _eventMouseY;
int _soundRate;
uint16 _eventKey;
int _soundRate;
int _musicVolume;
int32 _timers[50];
int16 getTimer(int16 timerNum);

View File

@ -5,6 +5,7 @@ MODULE_OBJS = \
detection.o \
graphics.o \
made.o \
music.o \
pmvplayer.o \
redreader.o \
resource.o \

274
engines/made/music.cpp Normal file
View File

@ -0,0 +1,274 @@
/* 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$
*
*/
// FIXME: This code is taken from SAGA and needs more work (e.g. setVolume).
// MIDI and digital music class
#include "sound/audiostream.h"
#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "common/config-manager.h"
#include "common/file.h"
#include "made/music.h"
namespace Made {
MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false), _passThrough(false), _isGM(false) {
memset(_channel, 0, sizeof(_channel));
_masterVolume = 0;
this->open();
}
MusicPlayer::~MusicPlayer() {
_driver->setTimerCallback(NULL, NULL);
stopMusic();
this->close();
}
void MusicPlayer::setVolume(int volume) {
volume = CLIP(volume, 0, 255);
if (_masterVolume == volume)
return;
_masterVolume = volume;
for (int i = 0; i < 16; ++i) {
if (_channel[i]) {
_channel[i]->volume(_channelVolume[i] * _masterVolume / 255);
}
}
}
int MusicPlayer::open() {
// Don't ever call open without first setting the output driver!
if (!_driver)
return 255;
int ret = _driver->open();
if (ret)
return ret;
_driver->setTimerCallback(this, &onTimer);
return 0;
}
void MusicPlayer::close() {
stopMusic();
if (_driver)
_driver->close();
_driver = 0;
}
void MusicPlayer::send(uint32 b) {
if (_passThrough) {
_driver->send(b);
return;
}
byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) {
// Adjust volume changes by master volume
byte volume = (byte)((b >> 16) & 0x7F);
_channelVolume[channel] = volume;
volume = volume * _masterVolume / 255;
b = (b & 0xFF00FFFF) | (volume << 16);
} else if ((b & 0xF0) == 0xC0 && !_isGM && !_nativeMT32) {
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
}
else if ((b & 0xFFF0) == 0x007BB0) {
//Only respond to All Notes Off if this channel
//has currently been allocated
if (_channel[b & 0x0F])
return;
}
if (!_channel[channel])
_channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
if (_channel[channel])
_channel[channel]->send(b);
}
void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) {
switch (type) {
case 0x2F: // End of Track
if (_looping)
_parser->jumpToTick(0);
else
stopMusic();
break;
default:
//warning("Unhandled meta event: %02x", type);
break;
}
}
void MusicPlayer::onTimer(void *refCon) {
MusicPlayer *music = (MusicPlayer *)refCon;
Common::StackLock lock(music->_mutex);
if (music->_isPlaying)
music->_parser->onTimer();
}
void MusicPlayer::playMusic() {
_isPlaying = true;
}
void MusicPlayer::stopMusic() {
Common::StackLock lock(_mutex);
_isPlaying = false;
if (_parser) {
_parser->unloadMusic();
_parser = NULL;
}
}
Music::Music(MidiDriver *driver, int enabled) : _enabled(enabled), _adlib(false) {
_player = new MusicPlayer(driver);
_currentVolume = 0;
xmidiParser = MidiParser::createParser_XMIDI();
_songTableLen = 0;
_songTable = 0;
_midiMusicData = NULL;
}
Music::~Music() {
delete _player;
xmidiParser->setMidiDriver(NULL);
delete xmidiParser;
free(_songTable);
if (_midiMusicData)
delete[] _midiMusicData;
}
void Music::setVolume(int volume, int time) {
_targetVolume = volume * 2; // ScummVM has different volume scale
_currentVolumePercent = 0;
if (volume == -1) // Set Full volume
volume = 255;
if (time == 1) {
_player->setVolume(volume);
_currentVolume = volume;
return;
}
}
bool Music::isPlaying() {
return _player->isPlaying();
}
void Music::play(XmidiResource *midiResource, MusicFlags flags) {
MidiParser *parser;
byte *resourceData;
size_t resourceSize;
debug(2, "Music::play %d", flags);
if (!_enabled) {
return;
}
if (isPlaying()) {
return;
}
_player->stopMusic();
/*
if (!_vm->_musicVolume) {
return;
}
*/
if (flags == MUSIC_DEFAULT) {
flags = MUSIC_NORMAL;
}
// Load MIDI/XMI resource data
_player->setGM(true);
resourceSize = midiResource->getSize();
resourceData = new byte[resourceSize];
memcpy(resourceData, midiResource->getData(), resourceSize);
if (resourceSize < 4) {
error("Music::play() wrong music resource size");
}
if (xmidiParser->loadMusic(resourceData, resourceSize)) {
//_player->setGM(false);
parser = xmidiParser;
}
parser->setTrack(0);
parser->setMidiDriver(_player);
parser->setTimerRate(_player->getBaseTempo());
parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
_player->_parser = parser;
//setVolume(_vm->_musicVolume == 10 ? 255 : _vm->_musicVolume * 25);
setVolume(255);
if (flags & MUSIC_LOOP)
_player->setLoop(true);
else
_player->setLoop(false);
_player->playMusic();
if (_midiMusicData)
delete[] _midiMusicData;
_midiMusicData = resourceData;
}
void Music::pause(void) {
_player->setVolume(-1);
_player->setPlaying(false);
}
void Music::resume(void) {
//_player->setVolume(_vm->_musicVolume == 10 ? 255 : _vm->_musicVolume * 25);
setVolume(255);
_player->setPlaying(true);
}
void Music::stop(void) {
_player->stopMusic();
}
} // End of namespace Made

150
engines/made/music.h Normal file
View File

@ -0,0 +1,150 @@
/* 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$
*
*/
// Music class
#ifndef MADE_MUSIC_H
#define MADE_MUSIC_H
#include "sound/audiocd.h"
#include "sound/mididrv.h"
#include "sound/midiparser.h"
#include "sound/mp3.h"
#include "sound/vorbis.h"
#include "sound/flac.h"
#include "common/mutex.h"
#include "made/resource.h"
namespace Made {
enum MusicFlags {
MUSIC_NORMAL = 0,
MUSIC_LOOP = 0x0001,
MUSIC_DEFAULT = 0xffff
};
class MusicPlayer : public MidiDriver {
public:
MusicPlayer(MidiDriver *driver);
~MusicPlayer();
bool isPlaying() { return _isPlaying; }
void setPlaying(bool playing) { _isPlaying = playing; }
void setVolume(int volume);
int getVolume() { return _masterVolume; }
void setNativeMT32(bool b) { _nativeMT32 = b; }
bool hasNativeMT32() { return _nativeMT32; }
void playMusic();
void stopMusic();
void setLoop(bool loop) { _looping = loop; }
void setPassThrough(bool b) { _passThrough = b; }
void setGM(bool isGM) { _isGM = isGM; }
//MidiDriver interface implementation
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(void) { return _driver ? _driver->getBaseTempo() : 0; }
//Channel allocation functions
MidiChannel *allocateChannel() { return 0; }
MidiChannel *getPercussionChannel() { return 0; }
MidiParser *_parser;
Common::Mutex _mutex;
protected:
static void onTimer(void *data);
MidiChannel *_channel[16];
MidiDriver *_driver;
byte _channelVolume[16];
bool _nativeMT32;
bool _isGM;
bool _passThrough;
bool _isPlaying;
bool _looping;
bool _randomLoop;
byte _masterVolume;
byte *_musicData;
uint16 *_buf;
size_t _musicDataSize;
};
class Music {
public:
Music(MidiDriver *driver, int enabled);
~Music(void);
void setNativeMT32(bool b) { _player->setNativeMT32(b); }
bool hasNativeMT32() { return _player->hasNativeMT32(); }
void setAdlib(bool b) { _adlib = b; }
bool hasAdlib() { return _adlib; }
void setPassThrough(bool b) { _player->setPassThrough(b); }
bool isPlaying(void);
void play(XmidiResource *midiResource, MusicFlags flags = MUSIC_DEFAULT);
void pause(void);
void resume(void);
void stop(void);
void setVolume(int volume, int time = 1);
int getVolume() { return _currentVolume; }
int32 *_songTable;
int _songTableLen;
private:
MusicPlayer *_player;
uint32 _trackNumber;
int _enabled;
bool _adlib;
int _targetVolume;
int _currentVolume;
int _currentVolumePercent;
MidiParser *xmidiParser;
byte *_midiMusicData;
};
} // End of namespace Made
#endif

View File

@ -205,6 +205,22 @@ const char *MenuResource::getString(uint index) const {
return NULL;
}
/* XmidiResource */
XmidiResource::XmidiResource() : _data(NULL), _size(0) {
}
XmidiResource::~XmidiResource() {
if (_data)
delete[] _data;
}
void XmidiResource::load(byte *source, int size) {
_data = new byte[size];
_size = size;
memcpy(_data, source, size);
}
/* ProjectReader */
ProjectReader::ProjectReader() {
@ -269,6 +285,10 @@ MenuResource *ProjectReader::getMenu(int index) {
return createResource<MenuResource>(kResMENU, index);
}
XmidiResource *ProjectReader::getXmidi(int index) {
return createResource<XmidiResource>(kResXMID, index);
}
void ProjectReader::loadIndex(ResourceSlots *slots) {
_fd->readUint32LE(); // skip INDX
_fd->readUint32LE(); // skip index size

View File

@ -44,7 +44,8 @@ enum ResourceType {
kResFLEX = MKID_BE('FLEX'),
kResSNDS = MKID_BE('SNDS'),
kResANIM = MKID_BE('ANIM'),
kResMENU = MKID_BE('MENU')
kResMENU = MKID_BE('MENU'),
kResXMID = MKID_BE('XMID')
};
struct ResourceSlot;
@ -110,6 +111,18 @@ protected:
Common::Array<Common::String> _strings;
};
class XmidiResource : public Resource {
public:
XmidiResource();
~XmidiResource();
void load(byte *source, int size);
byte *getData() const { return _data; }
int getSize() const { return _size; }
protected:
byte *_data;
int _size;
};
struct ResourceSlot {
uint32 offs;
uint32 size;
@ -133,6 +146,7 @@ public:
AnimationResource *getAnimation(int index);
SoundResource *getSound(int index);
MenuResource *getMenu(int index);
XmidiResource *getXmidi(int index);
void freeResource(Resource *resource);

View File

@ -472,4 +472,20 @@ void Screen::show() {
}
void Screen::flash(int flashCount) {
int palSize = _paletteColorCount * 3;
if (flashCount < 1)
flashCount = 1;
for (int i = 0; i < palSize; i++)
_fxPalette[i] = CLIP<byte>(255 - _palette[i], 0, 255);
while (flashCount--) {
setRGBPalette(_fxPalette, 0, _paletteColorCount);
_vm->_system->updateScreen();
_vm->_system->delayMillis(30);
setRGBPalette(_palette, 0, _paletteColorCount);
_vm->_system->updateScreen();
_vm->_system->delayMillis(30);
}
}
} // End of namespace Made

View File

@ -102,6 +102,7 @@ public:
uint16 placeText(uint16 channelIndex, uint16 textObjectIndex, int16 x, int16 y, uint16 fontNum, int16 textColor, int16 outlineColor);
void show();
void flash(int count);
byte _screenPalette[256 * 4];
@ -111,7 +112,7 @@ protected:
bool _screenLock;
bool _paletteLock;
byte _palette[768], _newPalette[768];
byte _palette[768], _newPalette[768], _fxPalette[768];
int _paletteColorCount, _oldPaletteColorCount;
bool _paletteInitialized, _needPalette;

View File

@ -36,6 +36,7 @@
#include "made/script.h"
#include "made/pmvplayer.h"
#include "made/scriptfuncs.h"
#include "made/music.h"
namespace Made {
@ -288,15 +289,25 @@ int16 ScriptFunctionsRtz::o1_PLAYSND(int16 argc, int16 *argv) {
}
int16 ScriptFunctionsRtz::o1_PLAYMUS(int16 argc, int16 *argv) {
int16 musicId = argv[0];
if (musicId > 0) {
XmidiResource *xmidi = _vm->_res->getXmidi(musicId);
_vm->_music->play(xmidi);
_vm->_res->freeResource(xmidi);
}
return 0;
}
int16 ScriptFunctionsRtz::o1_STOPMUS(int16 argc, int16 *argv) {
_vm->_music->stop();
return 0;
}
int16 ScriptFunctionsRtz::o1_ISMUS(int16 argc, int16 *argv) {
return 0;
if (_vm->_music->isPlaying())
return 1;
else
return 0;
}
int16 ScriptFunctionsRtz::o1_TEXTPOS(int16 argc, int16 *argv) {
@ -304,6 +315,7 @@ int16 ScriptFunctionsRtz::o1_TEXTPOS(int16 argc, int16 *argv) {
}
int16 ScriptFunctionsRtz::o1_FLASH(int16 argc, int16 *argv) {
_vm->_screen->flash(argv[0]);
return 0;
}
@ -536,10 +548,20 @@ int16 ScriptFunctionsRtz::o1_PLAYMOVIE(int16 argc, int16 *argv) {
}
int16 ScriptFunctionsRtz::o1_LOADSND(int16 argc, int16 *argv) {
SoundResource *sound = _vm->_res->getSound(argv[0]);
if (sound) {
_vm->_res->freeResource(sound);
return 1;
}
return 0;
}
int16 ScriptFunctionsRtz::o1_LOADMUS(int16 argc, int16 *argv) {
XmidiResource *xmidi = _vm->_res->getXmidi(argv[0]);
if (xmidi) {
_vm->_res->freeResource(xmidi);
return 1;
}
return 0;
}