1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ultima/ultima4/sound/music.h"
24 #include "ultima/ultima4/sound/sound.h"
25 #include "ultima/ultima4/core/config.h"
26 #include "ultima/ultima4/core/settings.h"
27 #include "ultima/ultima4/core/utils.h"
28 #include "ultima/ultima4/events/event_handler.h"
29 #include "ultima/ultima4/game/context.h"
30 #include "ultima/ultima4/map/location.h"
31 #include "ultima/ultima4/ultima4.h"
32 #include "ultima/shared/core/file.h"
33 #include "audio/decoders/mp3.h"
34 #include "audio/mods/mod_xm_s3m.h"
35 #include "audio/midiparser.h"
36 
37 namespace Ultima {
38 namespace Ultima4 {
39 
40 Music *g_music;
41 
Music(Audio::Mixer * mixer)42 Music::Music(Audio::Mixer *mixer) :
43 		Audio::MidiPlayer(), _mixer(mixer), _introMid(TOWNS) {
44 	g_music = this;
45 	Audio::MidiPlayer::createDriver();
46 
47 	int ret = _driver->open();
48 	if (ret == 0) {
49 		if (_nativeMT32)
50 			_driver->sendMT32Reset();
51 		else
52 			_driver->sendGMReset();
53 
54 		_driver->setTimerCallback(this, &timerCallback);
55 	}
56 
57 
58 	_filenames.reserve(MAX);
59 	_filenames.push_back("");    // filename for MUSIC_NONE;
60 
61 	/*
62 	 * load music track filenames from xml config file
63 	 */
64 	const Config *config = Config::getInstance();
65 
66 	Std::vector<ConfigElement> musicConfs = config->getElement("music").getChildren();
67 	Std::vector<ConfigElement>::const_iterator i = musicConfs.begin();
68 	Std::vector<ConfigElement>::const_iterator theEnd = musicConfs.end();
69 	for (; i != theEnd; ++i) {
70 		if (i->getName() != "track")
71 			continue;
72 
73 		_filenames.push_back(i->getString("file"));
74 	}
75 }
76 
~Music()77 Music::~Music() {
78 	stop();
79 	g_music = nullptr;
80 }
81 
sendToChannel(byte channel,uint32 b)82 void Music::sendToChannel(byte channel, uint32 b) {
83 	if (!_channelsTable[channel]) {
84 		_channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
85 		// If a new channel is allocated during the playback, make sure
86 		// its volume is correctly initialized.
87 		if (_channelsTable[channel])
88 			_channelsTable[channel]->volume(_channelsVolume[channel] * _masterVolume / 255);
89 	}
90 
91 	if (_channelsTable[channel])
92 		_channelsTable[channel]->send(b);
93 }
94 
playMusic(Type music)95 void Music::playMusic(Type music) {
96 	playMusic(_filenames[music]);
97 }
98 
playMapMusic()99 void Music::playMapMusic() {
100 	playMusic(g_context->_location->_map->_music);
101 }
102 
playMusic(const Common::String & filename)103 void Music::playMusic(const Common::String &filename) {
104 	stop();
105 
106 	// First try opening the file with whatever filename is provided
107 	if (startMusic(filename))
108 		return;
109 
110 	// TODO: Since the player doesn't yet support xu4 .it files,
111 	// try starting the file with other extensions - which some have
112 	const char *const EXTENSIONS[2] = { ".mp3", ".mid" };
113 	for (int idx = 0; idx < 2; ++idx) {
114 		size_t dotIndex = filename.findLastOf('.');
115 		Common::String fname = (dotIndex != Common::String::npos) ?
116 			Common::String(filename.c_str(), dotIndex) + EXTENSIONS[idx] :
117 			filename + EXTENSIONS[idx];
118 		if (startMusic(fname))
119 			return;
120 	}
121 
122 	// At this point, we couldn't open the given music file
123 	warning("No support for playing music file - %s", filename.c_str());
124 }
125 
startMusic(const Common::String & filename)126 bool Music::startMusic(const Common::String &filename) {
127 	Common::File musicFile;
128 	if (!musicFile.open(Common::String::format("data/mid/%s", filename.c_str())))
129 		// No such file exists
130 		return false;
131 
132 #ifdef USE_MAD
133 	if (filename.hasSuffixIgnoreCase(".mp3")) {
134 		Audio::SeekableAudioStream *audioStream = Audio::makeMP3Stream(
135 			musicFile.readStream(musicFile.size()), DisposeAfterUse::YES);
136 		_mixer->playStream(Audio::Mixer::kMusicSoundType,
137 			&_soundHandle, audioStream);
138 		return true;
139 
140 	} else
141 #endif
142 	if (filename.hasSuffixIgnoreCase(".mid")) {
143 		// Load MIDI resource data
144 		int midiMusicSize = musicFile.size();
145 		free(_midiData);
146 		_midiData = (byte *)malloc(midiMusicSize);
147 		musicFile.read(_midiData, midiMusicSize);
148 		musicFile.close();
149 
150 		MidiParser *parser = MidiParser::createParser_SMF();
151 		if (parser->loadMusic(_midiData, midiMusicSize)) {
152 			parser->setTrack(0);
153 			parser->setMidiDriver(this);
154 			parser->setTimerRate(_driver->getBaseTempo());
155 			parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
156 
157 			_parser = parser;
158 
159 			syncVolume();
160 
161 			_isLooping = false;
162 			_isPlaying = true;
163 			return true;
164 		} else {
165 			delete parser;
166 			return false;
167 		}
168 	} else {
169 		return false;
170 	}
171 }
172 
stop()173 void Music::stop() {
174 	_mixer->stopHandle(_soundHandle);
175 	Audio::MidiPlayer::stop();
176 }
177 
introSwitch(int n)178 void Music::introSwitch(int n) {
179 	if (n > NONE &&n < MAX) {
180 		_introMid = static_cast<Type>(n);
181 		intro();
182 	}
183 }
184 
185 } // End of namespace Ultima4
186 } // End of namespace Ultima
187