aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicola Mettifogo2009-03-14 17:02:28 +0000
committerNicola Mettifogo2009-03-14 17:02:28 +0000
commit6398e56d09976b21cca7f071e06caf51c7487615 (patch)
tree800f728c2736e1a1a089bff6f2d9967d0d4e7c55
parent68520809162c8e0212a4ae10fb178a21339fb87f (diff)
downloadscummvm-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.h3
-rw-r--r--engines/parallaction/disk_br.cpp2
-rw-r--r--engines/parallaction/module.mk1
-rw-r--r--engines/parallaction/parallaction.h4
-rw-r--r--engines/parallaction/parallaction_br.cpp13
-rw-r--r--engines/parallaction/parser_br.cpp7
-rw-r--r--engines/parallaction/sound.h29
-rw-r--r--engines/parallaction/sound_br.cpp462
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