diff options
author | Nicola Mettifogo | 2009-03-14 17:02:28 +0000 |
---|---|---|
committer | Nicola Mettifogo | 2009-03-14 17:02:28 +0000 |
commit | 6398e56d09976b21cca7f071e06caf51c7487615 (patch) | |
tree | 800f728c2736e1a1a089bff6f2d9967d0d4e7c55 | |
parent | 68520809162c8e0212a4ae10fb178a21339fb87f (diff) | |
download | scummvm-rg350-6398e56d09976b21cca7f071e06caf51c7487615.tar.gz scummvm-rg350-6398e56d09976b21cca7f071e06caf51c7487615.tar.bz2 scummvm-rg350-6398e56d09976b21cca7f071e06caf51c7487615.zip |
Added midi support to BRA. So far music starts, but related script commands haven't been implemented yet.
svn-id: r39397
-rw-r--r-- | engines/parallaction/disk.h | 3 | ||||
-rw-r--r-- | engines/parallaction/disk_br.cpp | 2 | ||||
-rw-r--r-- | engines/parallaction/module.mk | 1 | ||||
-rw-r--r-- | engines/parallaction/parallaction.h | 4 | ||||
-rw-r--r-- | engines/parallaction/parallaction_br.cpp | 13 | ||||
-rw-r--r-- | engines/parallaction/parser_br.cpp | 7 | ||||
-rw-r--r-- | engines/parallaction/sound.h | 29 | ||||
-rw-r--r-- | engines/parallaction/sound_br.cpp | 462 |
8 files changed, 509 insertions, 12 deletions
diff --git a/engines/parallaction/disk.h b/engines/parallaction/disk.h index 441b2468b3..63013ce9e6 100644 --- a/engines/parallaction/disk.h +++ b/engines/parallaction/disk.h @@ -48,6 +48,9 @@ struct Frames; struct Cnv; struct Sprites; struct BackgroundInfo; +struct GfxObj; +struct MaskBuffer; +struct PathBuffer; class Disk { diff --git a/engines/parallaction/disk_br.cpp b/engines/parallaction/disk_br.cpp index 0dca43a55c..cd98106f54 100644 --- a/engines/parallaction/disk_br.cpp +++ b/engines/parallaction/disk_br.cpp @@ -395,7 +395,7 @@ Table* DosDisk_br::loadTable(const char* name) { Common::SeekableReadStream* DosDisk_br::loadMusic(const char* name) { debugC(5, kDebugDisk, "DosDisk_br::loadMusic"); - return 0; + return openFile("msc/" + Common::String(name), ".msc"); } diff --git a/engines/parallaction/module.mk b/engines/parallaction/module.mk index 29e4863fec..3bda5a95ec 100644 --- a/engines/parallaction/module.mk +++ b/engines/parallaction/module.mk @@ -29,6 +29,7 @@ MODULE_OBJS := \ parser_ns.o \ saveload.o \ sound_ns.o \ + sound_br.o \ staticres.o \ walk.o diff --git a/engines/parallaction/parallaction.h b/engines/parallaction/parallaction.h index 4aaa3dd0c6..0e35182449 100644 --- a/engines/parallaction/parallaction.h +++ b/engines/parallaction/parallaction.h @@ -125,6 +125,7 @@ class CommandExec; class ProgramExec; class SoundMan; class SoundMan_ns; +class SoundMan_br; struct Location { @@ -533,7 +534,8 @@ public: private: LocationParser_br *_locationParser; ProgramParser_br *_programParser; - + SoundMan_br *_soundManI; + int32 _counters[32]; Table *_countersNames; diff --git a/engines/parallaction/parallaction_br.cpp b/engines/parallaction/parallaction_br.cpp index 5f2ced4d1f..28a36db6cd 100644 --- a/engines/parallaction/parallaction_br.cpp +++ b/engines/parallaction/parallaction_br.cpp @@ -45,7 +45,7 @@ const char *Parallaction_br::_partNames[] = { }; Parallaction_br::Parallaction_br(OSystem* syst, const PARALLACTIONGameDescription *gameDesc) : Parallaction(syst, gameDesc), - _locationParser(0), _programParser(0) { + _locationParser(0), _programParser(0), _soundManI(0) { } Common::Error Parallaction_br::init() { @@ -53,8 +53,6 @@ Common::Error Parallaction_br::init() { _screenWidth = 640; _screenHeight = 400; - SoundManImpl* _soundManI = 0; - if (getPlatform() == Common::kPlatformPC) { if (getFeatures() & GF_DEMO) { _disk = new DosDemoDisk_br(this); @@ -62,11 +60,12 @@ Common::Error Parallaction_br::init() { _disk = new DosDisk_br(this); } _disk->setLanguage(2); // NOTE: language is now hardcoded to English. Original used command-line parameters. - _soundManI = new DummySoundMan(); + int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI); + MidiDriver *driver = MidiDriver::createMidi(midiDriver); + _soundManI = new DosSoundMan_br(this, driver); } else { _disk = new AmigaDisk_br(this); _disk->setLanguage(2); // NOTE: language is now hardcoded to English. Original used command-line parameters. - _soundManI = new DummySoundMan(); } _disk->init(); @@ -301,6 +300,10 @@ void Parallaction_br::changeLocation() { doLocationEnterTransition(); _cmdExec->run(_location._aCommands); + + // NOTE: music should not started here! + // TODO: implement the music commands which control music execution + _soundMan->execute(SC_PLAYMUSIC); _engineFlags &= ~kEngineChangeLocation; _newLocationName.clear(); diff --git a/engines/parallaction/parser_br.cpp b/engines/parallaction/parser_br.cpp index 37084833a8..141048caa5 100644 --- a/engines/parallaction/parser_br.cpp +++ b/engines/parallaction/parser_br.cpp @@ -421,16 +421,13 @@ DECLARE_LOCATION_PARSER(endcomment) { DECLARE_LOCATION_PARSER(sound) { - debugC(7, kDebugParser, "LOCATION_PARSER(sound) "); - -// _soundMan->loadSound(_tokens[1]); + warning("SOUND command unexpected when parsing location"); } DECLARE_LOCATION_PARSER(music) { debugC(7, kDebugParser, "LOCATION_PARSER(music) "); - -// _soundMan->loadMusic(_tokens[1]); + _vm->_soundMan->execute(SC_SETMUSICFILE, _tokens[1]); } DECLARE_LOCATION_PARSER(redundant) { diff --git a/engines/parallaction/sound.h b/engines/parallaction/sound.h index 72d22e02b4..57e86ce159 100644 --- a/engines/parallaction/sound.h +++ b/engines/parallaction/sound.h @@ -42,6 +42,9 @@ namespace Parallaction { class Parallaction_ns; class MidiPlayer; +class Parallaction_br; +class MidiPlayer_MSC; + class SoundManImpl { public: @@ -177,6 +180,32 @@ public: void execute(int command, const char *parm) { } }; +class SoundMan_br : public SoundManImpl { +protected: + Common::String _musicFile; + + virtual void playMusic() = 0; + virtual void stopMusic() = 0; + virtual void pause(bool p) = 0; + +public: + virtual void execute(int command, const char *parm); + void setMusicFile(const char *parm); +}; + +class DosSoundMan_br : public SoundMan_br { + + MidiPlayer_MSC *_midiPlayer; + +public: + DosSoundMan_br(Parallaction_br *vm, MidiDriver *midiDriver); + ~DosSoundMan_br(); + + void playMusic(); + void stopMusic(); + void pause(bool p); +}; + } // namespace Parallaction #endif diff --git a/engines/parallaction/sound_br.cpp b/engines/parallaction/sound_br.cpp new file mode 100644 index 0000000000..e2c01520f9 --- /dev/null +++ b/engines/parallaction/sound_br.cpp @@ -0,0 +1,462 @@ +/* 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 "sound/mixer.h" +#include "common/stream.h" +#include "common/util.h" +#include "sound/mididrv.h" +#include "sound/midiparser.h" + +#include "parallaction/disk.h" +#include "parallaction/parallaction.h" +#include "parallaction/sound.h" + + +namespace Parallaction { + + +/* + * List of calls to the original music driver. + * + * + * 1 set music buffer segment + * 2 set music buffer offset + * 3 set music buffer size + * 4 play/resume + * 5 stop + * 6 pause + * 7 set channel volume + * 8 set byte_11C5A (boolean flag for ??) + * 9 toggle fade + * 10 set volume + * 11 shutdown + * 12 get status + * 13 set byte_11C4D (used for fade??) + * 14 get volume + * 15 get X?? + * 16 get fade flag + * 17 set tempo + * 18 get tempo + * 19 set Y?? + * 20 get looping flag + * 21 toggle looping flag + * 22 get version?? + * 23 get version?? + * 24 get busy flag + */ + +class MidiParser_MSC : public MidiParser { +protected: + virtual void parseNextEvent(EventInfo &info); + virtual bool loadMusic(byte *data, uint32 size); + + uint8 read1(byte *&data) { + return *data++; + } + + void parseMetaEvent(EventInfo &info); + void parseMidiEvent(EventInfo &info); + + bool byte_11C5A; + uint8 _beats; + uint8 _lastEvent; + byte *_trackEnd; + +public: + MidiParser_MSC() : byte_11C5A(false) { + } +}; + +void MidiParser_MSC::parseMetaEvent(EventInfo &info) { + uint8 type = read1(_position._play_pos); + uint8 len = read1(_position._play_pos); + info.ext.type = type; + info.length = len; + info.ext.data = 0; + + if (type == 0x51) { + info.ext.data = _position._play_pos; + } else { + warning("unknown meta event 0x%02X\n", type); + info.ext.type = 0; + } + + _position._play_pos += len; +} + +void MidiParser_MSC::parseMidiEvent(EventInfo &info) { + uint8 type = info.command(); + + switch (type) { + case 0x8: + case 0x9: + case 0xA: + case 0xB: + case 0xE: + info.basic.param1 = read1(_position._play_pos); + info.basic.param2 = read1(_position._play_pos); + break; + + case 0xC: + case 0xD: + info.basic.param1 = read1(_position._play_pos); + info.basic.param2 = 0; + break; + + default: + warning("Unexpected midi event 0x%02X in midi data.", info.event); + } + + //if ((type == 0xB) && (info.basic.param1 == 64)) info.basic.param2 = 127; + +} + +void MidiParser_MSC::parseNextEvent(EventInfo &info) { + info.start = _position._play_pos; + + if (_position._play_pos >= _trackEnd) { + // fake an end-of-track meta event + info.delta = 0; + info.event = 0xFF; + info.ext.type = 0x2F; + info.length = 0; + return; + } + + info.delta = readVLQ(_position._play_pos); + info.event = read1(_position._play_pos); + + if (info.event == 0xFF) { + parseMetaEvent(info); + return; + } + + if (info.event < 0x80) { + _position._play_pos--; + info.event = _lastEvent; + } + + parseMidiEvent(info); + _lastEvent = info.event; + +} + +bool MidiParser_MSC::loadMusic(byte *data, uint32 size) { + unloadMusic(); + + byte *pos = data; + + uint32 signature = read4high(pos); + if (memcmp("tCSM", &signature, 4)) { + warning("Expected header not found in music file."); + return false; + } + + _beats = read1(pos); + _ppqn = read2low(pos); + + if (byte_11C5A) { + // do something with byte_11C4D + } + + _lastEvent = 0; + _trackEnd = data + size; + + _num_tracks = 1; + _tracks[0] = pos; + + setTempo(500000); + setTrack(0); + return true; +} + + +MidiParser *createParser_MSC() { + return new MidiParser_MSC; +} + + +class MidiPlayer_MSC : public MidiDriver { +public: + + enum { + NUM_CHANNELS = 16 + }; + + MidiPlayer_MSC(MidiDriver *driver); + ~MidiPlayer_MSC(); + + void play(Common::SeekableReadStream *stream); + void stop(); + void pause(bool p); + void updateTimer(); + void adjustVolume(int diff); + void setVolume(int volume); + int getVolume() const { return _masterVolume; } + void setLooping(bool loop) { _isLooping = loop; } + + // 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); + void setVolumeInternal(int volume); + + Common::Mutex _mutex; + MidiDriver *_driver; + MidiParser *_parser; + uint8 *_midiData; + bool _isLooping; + bool _isPlaying; + bool _paused; + + int _masterVolume; + MidiChannel *_channels[NUM_CHANNELS]; + uint8 _volume[NUM_CHANNELS]; +}; + + + +MidiPlayer_MSC::MidiPlayer_MSC(MidiDriver *driver) + : _driver(driver), _parser(0), _midiData(0), _isLooping(false), _isPlaying(false), _paused(false), _masterVolume(0) { + assert(_driver); + memset(_channels, 0, sizeof(_channels)); + for (int i = 0; i < NUM_CHANNELS; i++) { + _volume[i] = 127; + } + + open(); +} + +MidiPlayer_MSC::~MidiPlayer_MSC() { + close(); +} + +void MidiPlayer_MSC::play(Common::SeekableReadStream *stream) { + if (!stream) { + stop(); + return; + } + + int size = stream->size(); + + _midiData = (uint8 *)malloc(size); + if (_midiData) { + stream->read(_midiData, size); + delete stream; + _mutex.lock(); + _parser->loadMusic(_midiData, size); + _parser->setTrack(0); + _isLooping = true; + _isPlaying = true; + _mutex.unlock(); + } +} + +void MidiPlayer_MSC::stop() { + _mutex.lock(); + if (_isPlaying) { + _isPlaying = false; + _parser->unloadMusic(); + free(_midiData); + _midiData = 0; + } + _mutex.unlock(); +} + +void MidiPlayer_MSC::pause(bool p) { + _paused = p; + setVolumeInternal(_paused ? 0 : _masterVolume); +} + +void MidiPlayer_MSC::updateTimer() { + if (_paused) { + return; + } + + Common::StackLock lock(_mutex); + if (_isPlaying) { + _parser->onTimer(); + } +} + +void MidiPlayer_MSC::adjustVolume(int diff) { + setVolume(_masterVolume + diff); +} + +void MidiPlayer_MSC::setVolume(int volume) { + _masterVolume = CLIP(volume, 0, 255); + setVolumeInternal(_masterVolume); +} + +void MidiPlayer_MSC::setVolumeInternal(int volume) { + Common::StackLock lock(_mutex); + for (int i = 0; i < NUM_CHANNELS; ++i) { + if (_channels[i]) { + _channels[i]->volume(_volume[i] * volume / 255); + } + } +} + +int MidiPlayer_MSC::open() { + int ret = _driver->open(); + if (ret == 0) { + _parser = createParser_MSC(); + _parser->setMidiDriver(this); + _parser->setTimerRate(_driver->getBaseTempo()); + _driver->setTimerCallback(this, &timerCallback); + } + return ret; +} + +void MidiPlayer_MSC::close() { + stop(); + _mutex.lock(); + _driver->setTimerCallback(NULL, NULL); + _driver->close(); + delete _driver; + _driver = 0; + _parser->setMidiDriver(NULL); + delete _parser; + _mutex.unlock(); +} + +void MidiPlayer_MSC::send(uint32 b) { + const byte ch = b & 0x0F; + byte param2 = (b >> 16) & 0xFF; + + switch (b & 0xFFF0) { + case 0x07B0: // volume change + _volume[ch] = param2; + break; + } + + if (!_channels[ch]) { + _channels[ch] = (ch == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + } + if (_channels[ch]) { + _channels[ch]->send(b); + } +} + +void MidiPlayer_MSC::metaEvent(byte type, byte *data, uint16 length) { + switch (type) { + case 0x2F: // end of Track + if (_isLooping) { + _parser->jumpToTick(0); + } else { + stop(); + } + break; + default: + break; + } +} + +void MidiPlayer_MSC::timerCallback(void *p) { + MidiPlayer_MSC *player = (MidiPlayer_MSC *)p; + + player->updateTimer(); +} + +DosSoundMan_br::DosSoundMan_br(Parallaction_br *vm, MidiDriver *driver) { + _midiPlayer = new MidiPlayer_MSC(driver); + assert(_midiPlayer); +} + +DosSoundMan_br::~DosSoundMan_br() { + delete _midiPlayer; +} + +void DosSoundMan_br::playMusic() { + if (_musicFile.empty()) { + return; + } + + Common::SeekableReadStream *s = _vm->_disk->loadMusic(_musicFile.c_str()); + assert(s); + _midiPlayer->play(s); +} + +void DosSoundMan_br::stopMusic() { + _midiPlayer->stop(); +} + +void DosSoundMan_br::pause(bool p) { + _midiPlayer->pause(p); +} + +void SoundMan_br::setMusicFile(const char *name) { + _musicFile = name; +} + +void SoundMan_br::execute(int command, const char *parm) { + uint32 n = parm ? strtoul(parm, 0, 10) : 0; + bool b = (n == 1) ? true : false; + + switch (command) { + case SC_PLAYMUSIC: + playMusic(); + break; + case SC_STOPMUSIC: + stopMusic(); + break; + case SC_SETMUSICFILE: + setMusicFile(parm); + break; + + case SC_PLAYSFX: + warning("SC_PLAYSFX not yet supported!"); + break; + case SC_STOPSFX: + warning("SC_STOPSFX not yet supported!"); + break; + + case SC_SETSFXCHANNEL: + warning("SC_SETSFXCHANNEL not yet supported!"); + break; + case SC_SETSFXLOOPING: + warning("SC_SETSFXLOOPING not yet supported!"); + break; + case SC_SETSFXVOLUME: + warning("SC_SETSFXVOLUME not yet supported!"); + break; + + case SC_PAUSE: + pause(b); + break; + } +} + + +} // namespace Parallaction |