aboutsummaryrefslogtreecommitdiff
path: root/engines/sherlock/music.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sherlock/music.cpp')
-rw-r--r--engines/sherlock/music.cpp360
1 files changed, 360 insertions, 0 deletions
diff --git a/engines/sherlock/music.cpp b/engines/sherlock/music.cpp
new file mode 100644
index 0000000000..0735f41d9a
--- /dev/null
+++ b/engines/sherlock/music.cpp
@@ -0,0 +1,360 @@
+/* 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.
+ *
+ */
+
+#include "common/config-manager.h"
+#include "sherlock/sherlock.h"
+#include "sherlock/music.h"
+#include "sherlock/scalpel/drivers/mididriver.h"
+
+namespace Sherlock {
+
+#define NUM_SONGS 45
+
+/* This tells which song to play in each room, 0 = no song played */
+static const char ROOM_SONG[62] = {
+ 0, 20, 43, 6, 11, 2, 8, 15, 6, 28,
+ 6, 38, 7, 32, 16, 5, 8, 41, 9, 22,
+ 10, 23, 4, 39, 19, 24, 13, 27, 0, 30,
+ 3, 21, 26, 25, 16, 29, 1, 1, 18, 12,
+ 1, 17, 17, 31, 17, 34, 36, 7, 20, 20,
+ 33, 8, 44, 40, 42, 35, 0, 0, 0, 12,
+ 12
+};
+
+static const char *const SONG_NAMES[NUM_SONGS] = {
+ "SINGERF", "CHEMIST", "TOBAC", "EQUEST", "MORTUARY", "DOCKS", "LSTUDY",
+ "LORD", "BOY", "PERFUM1", "BAKER1", "BAKER2", "OPERA1", "HOLMES",
+ "FFLAT", "OP1FLAT", "ZOO", "SROOM", "FLOWERS", "YARD", "TAXID",
+ "PUB1", "VICTIM", "RUGBY", "DORM", "SHERMAN", "LAWYER", "THEATRE",
+ "DETECT", "OPERA4", "POOL", "SOOTH", "ANNA1", "ANNA2", "PROLOG3",
+ "PAWNSHOP", "MUSICBOX", "MOZART1", "ROBHUNT", "PANCRAS1", "PANCRAS2", "LORDKILL",
+ "BLACKWEL", "RESCUE", "MAP"
+};
+
+MidiParser_SH::MidiParser_SH() {
+ _ppqn = 1;
+ setTempo(16667);
+ _data = nullptr;
+ _beats = 0;
+ _lastEvent = 0;
+ _trackEnd = nullptr;
+}
+
+void MidiParser_SH::parseNextEvent(EventInfo &info) {
+// warning("parseNextEvent");
+
+ // An attempt to remap MT32 instruments to GMIDI. Only partially successful, it still
+ // does not sound even close to the real MT32. Oddly enough, on the actual hardware MT32
+ // and SB sound very differently.
+ static const byte mt32Map[128] = {
+ 0, 1, 0, 2, 4, 4, 5, 3, /* 0-7 */
+ 16, 17, 18, 16, 16, 19, 20, 21, /* 8-15 */
+ 6, 6, 6, 7, 7, 7, 8, 112, /* 16-23 */
+ 62, 62, 63, 63 , 38, 38, 39, 39, /* 24-31 */
+ 88, 95, 52, 98, 97, 99, 14, 54, /* 32-39 */
+ 102, 96, 53, 102, 81, 100, 14, 80, /* 40-47 */
+ 48, 48, 49, 45, 41, 40, 42, 42, /* 48-55 */
+ 43, 46, 45, 24, 25, 28, 27, 104, /* 56-63 */
+ 32, 32, 34, 33, 36, 37, 35, 35, /* 64-71 */
+ 79, 73, 72, 72, 74, 75, 64, 65, /* 72-79 */
+ 66, 67, 71, 71, 68, 69, 70, 22, /* 80-87 */
+ 56, 59, 57, 57, 60, 60, 58, 61, /* 88-95 */
+ 61, 11, 11, 98, 14, 9, 14, 13, /* 96-103 */
+ 12, 107, 107, 77, 78, 78, 76, 76, /* 104-111 */
+ 47, 117, 127, 118, 118, 116, 115, 119, /* 112-119 */
+ 115, 112, 55, 124, 123, 0, 14, 117 /* 120-127 */
+ };
+
+ // there is no delta right at the start of the music data
+ // this order is essential, otherwise notes will get delayed or even go missing
+ if (_position._playPos != _tracks[0]) {
+ info.delta = *(_position._playPos++);
+ } else {
+ info.delta = 0;
+ }
+
+ info.start = _position._playPos;
+
+ info.event = *_position._playPos++;
+ //warning("Event %x", info.event);
+ _position._runningStatus = info.event;
+
+ switch (info.command()) {
+ case 0xC: { // program change
+ int idx = *_position._playPos++;
+ info.basic.param1 = idx & 0x7f;
+ // don't do this here, it breaks adlib
+ //info.basic.param1 = mt32Map[idx & 0x7f]; // remap MT32 to GM
+ info.basic.param2 = 0;
+ }
+ break;
+ case 0xD:
+ info.basic.param1 = *_position._playPos++;
+ info.basic.param2 = 0;
+ break;
+
+ case 0xB:
+ info.basic.param1 = *_position._playPos++;
+ info.basic.param2 = *_position._playPos++;
+ info.length = 0;
+ break;
+
+ case 0x8:
+ case 0x9:
+ case 0xA:
+ case 0xE:
+ info.basic.param1 = *(_position._playPos++);
+ info.basic.param2 = *(_position._playPos++);
+ if (info.command() == 0x9 && info.basic.param2 == 0) {
+ // NoteOn with param2==0 is a NoteOff
+ info.event = info.channel() | 0x80;
+ }
+ info.length = 0;
+ break;
+ case 0xF:
+ if (info.event == 0xFF) {
+ error("SysEx META event 0xFF");
+
+ byte type = *(_position._playPos++);
+ switch(type) {
+ case 0x2F:
+ // End of Track
+ allNotesOff();
+ stopPlaying();
+ unloadMusic();
+ return;
+ case 0x51:
+ warning("TODO: 0xFF / 0x51");
+ return;
+ default:
+ warning("TODO: 0xFF / %x Unknown", type);
+ break;
+ }
+ } else if (info.event == 0xFC) {
+ // Official End-Of-Track signal
+ warning("System META event 0xFC");
+
+ byte type = *(_position._playPos++);
+ switch (type) {
+ case 0x80: // end of track, triggers looping
+ warning("META event triggered looping");
+ jumpToTick(0, true, true, false);
+ break;
+ case 0x81: // end of track, stop playing
+ warning("META event triggered music stop");
+ stopPlaying();
+ unloadMusic();
+ break;
+ default:
+ error("MidiParser_SH::parseNextEvent: Unknown META event 0xFC type %x", type);
+ break;
+ }
+ } else {
+ warning("TODO: %x / Unknown", info.event);
+ break;
+ }
+ break;
+ default:
+ warning("MidiParser_SH::parseNextEvent: Unsupported event code %x", info.event);
+ break;
+ }// switch (info.command())
+}
+
+bool MidiParser_SH::loadMusic(byte *data, uint32 size) {
+ warning("loadMusic");
+ unloadMusic();
+
+ byte *headerPtr = data;
+ byte *pos = data;
+ uint16 headerSize = READ_LE_UINT16(headerPtr);
+ assert(headerSize == 0x7F);
+
+ // Skip over header
+ pos += headerSize;
+
+ _lastEvent = 0;
+ _trackEnd = data + size;
+
+ _numTracks = 1;
+ _tracks[0] = pos;
+
+ _ppqn = 1;
+ setTempo(16667);
+ setTrack(0);
+
+ return true;
+}
+
+/*----------------------------------------------------------------*/
+
+Music::Music(SherlockEngine *vm, Audio::Mixer *mixer) : _vm(vm), _mixer(mixer) {
+ if (_vm->_interactiveFl)
+ _vm->_res->addToCache("MUSIC.LIB");
+
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+
+ _musicType = MidiDriver::getMusicType(dev);
+
+ _driver = NULL;
+
+ switch (_musicType) {
+ case MT_ADLIB:
+ _driver = MidiDriver_AdLib_create();
+ break;
+ default:
+ _driver = MidiDriver::createMidi(dev);
+ break;
+ }
+
+ if (_driver) {
+ assert(_driver);
+
+ int ret = _driver->open();
+ if (ret == 0) {
+ _driver->sendGMReset();
+ _driver->setTimerCallback(&_midiParser, &_midiParser.timerCallback);
+ }
+ _midiParser.setMidiDriver(_driver);
+ _midiParser.setTimerRate(_driver->getBaseTempo());
+ }
+
+ _musicPlaying = false;
+ _musicOn = true;
+}
+
+bool Music::loadSong(int songNumber) {
+ warning("loadSong");
+
+ if(songNumber == 100)
+ songNumber = 55;
+ else if(songNumber == 70)
+ songNumber = 54;
+
+ if((songNumber > 60) || (songNumber < 1))
+ return false;
+
+ songNumber = ROOM_SONG[songNumber];
+
+ if(songNumber == 0)
+ songNumber = 12;
+
+ if((songNumber > NUM_SONGS) || (songNumber < 1))
+ return false;
+
+ Common::String songName = Common::String(SONG_NAMES[songNumber - 1]) + ".MUS";
+
+ freeSong(); // free any song that is currently loaded
+
+ if (!playMusic(songName))
+ return false;
+
+ stopMusic();
+ startSong();
+ return true;
+}
+
+bool Music::loadSong(const Common::String &songName) {
+ warning("TODO: Music::loadSong");
+ return false;
+}
+
+void Music::syncMusicSettings() {
+ _musicOn = !ConfMan.getBool("mute") && !ConfMan.getBool("music_mute");
+}
+
+bool Music::playMusic(const Common::String &name) {
+ if (!_musicOn)
+ return false;
+
+ warning("Sound::playMusic %s", name.c_str());
+ Common::SeekableReadStream *stream = _vm->_res->load(name, "MUSIC.LIB");
+
+ byte *data = new byte[stream->size()];
+ int32 dataSize = stream->size();
+ assert(data);
+
+ stream->read(data, dataSize);
+ delete stream;
+
+ // for dumping the music tracks
+#if 0
+ Common::DumpFile outFile;
+ outFile.open(name + ".RAW");
+ outFile.write(data, stream->size());
+ outFile.flush();
+ outFile.close();
+#endif
+
+ if (dataSize < 14) {
+ warning("not enough data in music file");
+ return false;
+ }
+
+ byte *dataPos = data;
+ if (memcmp(" ", dataPos, 12)) {
+ warning("Expected header not found in music file");
+ return false;
+ }
+ dataPos += 12;
+ dataSize -= 12;
+
+ uint16 headerSize = READ_LE_UINT16(dataPos);
+ if (headerSize != 0x7F) {
+ warning("music header is not as expected");
+ return false;
+ }
+
+ if (_musicType == MT_ADLIB) {
+ if (_driver)
+ MidiDriver_AdLib_newMusicData(_driver, dataPos, dataSize);
+ }
+
+ _midiParser.loadMusic(dataPos, dataSize);
+ return true;
+}
+
+void Music::stopMusic() {
+ // TODO
+ warning("TODO: Sound::stopMusic");
+
+ _musicPlaying = false;
+}
+
+void Music::startSong() {
+ if (!_musicOn)
+ return;
+
+ // TODO
+ warning("TODO: Sound::startSong");
+ _musicPlaying = true;
+}
+
+void Music::freeSong() {
+ // TODO
+ warning("TODO: Sound::freeSong");
+}
+
+void Music::waitTimerRoland(uint time) {
+ // TODO
+ warning("TODO: Sound::waitTimerRoland");
+}} // End of namespace Sherlock
+