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 #ifdef ENABLE_HE
24 
25 #include "scumm/players/player_he.h"
26 #include "scumm/scumm.h"
27 #include "scumm/file.h"
28 #include "audio/miles.h"
29 #include "audio/midiparser.h"
30 #include "audio/mixer.h"
31 #include "common/memstream.h"
32 
33 namespace Scumm {
Player_HE(ScummEngine * scumm)34 Player_HE::Player_HE(ScummEngine *scumm) :
35 	_vm(scumm),
36 	_currentMusic(-1),
37 	_bank(NULL),
38 	_parser(NULL),
39 	_midi(NULL),
40 	_masterVolume(256) {
41 
42 	for (int chan = 0; chan < 16; chan++)
43 		_channelVolume[chan] = 127;
44 
45 	loadAdLibBank();
46 
47 	Common::MemoryReadStream *bankStream = new Common::MemoryReadStream(_bank, _bankSize);
48 
49 	_midi = Audio::MidiDriver_Miles_AdLib_create("", "", bankStream);
50 	if (!_midi) {
51 		error("Player_HE::Player_HE: could not create midi driver");
52 	}
53 	if (_midi->open() != 0) {
54 		error("Player_HE::Player_HE: could not open midi driver");
55 	}
56 }
57 
~Player_HE()58 Player_HE::~Player_HE() {
59 	if (_parser) {
60 		_parser->stopPlaying();
61 		delete _parser;
62 		_parser = NULL;
63 	}
64 	if (_midi) {
65 		_midi->setTimerCallback(0, 0);
66 		_midi->close();
67 		delete _midi;
68 		_midi = NULL;
69 	}
70 	if (_bank) {
71 		free(_bank);
72 	}
73 }
74 
setMusicVolume(int vol)75 void Player_HE::setMusicVolume(int vol) {
76 	_masterVolume = vol;
77 	for (int chan = 0; chan < 16; chan++) {
78 		byte volume = (_channelVolume[chan] * vol) / 256;
79 		if (_midi)
80 			_midi->send(0x07b0 | chan | (volume << 16));
81 	}
82 }
83 
onTimer(void * data)84 void Player_HE::onTimer(void *data) {
85 	Player_HE *player = (Player_HE *)data;
86 	Common::StackLock lock(player->_mutex);
87 	if (player->_parser)
88 		player->_parser->onTimer();
89 }
90 
startSoundWithTrackID(int sound,int track)91 void Player_HE::startSoundWithTrackID(int sound, int track) {
92 	Common::StackLock lock(_mutex);
93 	byte *ptr = _vm->getResourceAddress(rtSound, sound);
94 	if (ptr == NULL)
95 		return;
96 
97 	if (_parser) {
98 		_parser->stopPlaying();
99 		delete _parser;
100 	}
101 	_parser = MidiParser::createParser_XMIDI();
102 	_parser->setMidiDriver(this);
103 	_parser->loadMusic(ptr + 40, 0);
104 	_parser->setTrack(track);
105 	_parser->setTimerRate(_midi->getBaseTempo());
106 	_midi->setTimerCallback(this, &Player_HE::onTimer);
107 
108 	_currentMusic = sound;
109 }
110 
stopSound(int sound)111 void Player_HE::stopSound(int sound) {
112 	Common::StackLock lock(_mutex);
113 	if (!_parser || _currentMusic != sound)
114 		return;
115 	_parser->stopPlaying();
116 	delete _parser;
117 	_parser = NULL;
118 }
119 
stopAllSounds()120 void Player_HE::stopAllSounds() {
121 	Common::StackLock lock(_mutex);
122 	if (!_parser)
123 		return;
124 	_parser->stopPlaying();
125 	delete _parser;
126 	_parser = NULL;
127 }
128 
getSoundStatus(int sound) const129 int Player_HE::getSoundStatus(int sound) const {
130 	Common::StackLock lock(_mutex);
131 	return (_parser && _currentMusic == sound) ? _parser->isPlaying() : 0;
132 }
133 
getMusicTimer()134 int Player_HE::getMusicTimer() {
135 	Common::StackLock lock(_mutex);
136 	return _parser ? _parser->getTick() : 0;
137 }
138 
loadAdLibBank()139 void Player_HE::loadAdLibBank() {
140 	ScummFile file;
141 	Common::String drvName;
142 	char entryName[14];
143 	uint32 tag, entrySize, fileSize;
144 	Common::String bankName;
145 
146 	if (_vm->_game.id == GID_PUTTMOON) {
147 		// Use GM bank
148 		bankName = "FAT.AD";
149 	} else {
150 		// Use MT32-like bank
151 		bankName = "MIDPAK.AD";
152 	}
153 
154 	const char *ptr = strchr(_vm->_filenamePattern.pattern, '.');
155 	if (ptr) {
156 		drvName = Common::String(_vm->_filenamePattern.pattern, ptr - _vm->_filenamePattern.pattern + 1);
157 	} else {
158 		drvName = _vm->_filenamePattern.pattern;
159 		drvName += '.';
160 	}
161 
162 	drvName += "drv";
163 
164 	if (!file.open(drvName))
165 		error("Player_HE::loadAdLibBank(): could not open %s", drvName.c_str());
166 
167 	uint32 size = (uint32)file.size();
168 
169 	for (uint32 offset = 0; offset < size;) {
170 		file.seek(offset, SEEK_SET);
171 		if (size - offset < 31)
172 			error("Player_HE::loadAdLibBank(): unexpected end of file");
173 
174 		tag = file.readUint32BE();
175 		entrySize = file.readUint32BE();
176 		if (size - offset < entrySize)
177 			error("Player_HE::loadAdLibBank(): unexpected end of file");
178 		fileSize = entrySize - 31;
179 		file.read(entryName, 13);
180 		entryName[13] = 0;
181 
182 		if (tag != MKTAG('F', 'I', 'L', 'E'))
183 			error("Player_HE::loadAdLibBank(): unknown entry format");
184 
185 		if (entryName == bankName) {
186 			_bank = (byte*)malloc(fileSize);
187 			file.read(_bank, fileSize);
188 			_bankSize = fileSize;
189 			return;
190 		}
191 
192 		offset += entrySize;
193 	}
194 	error("Player_HE::loadAdLibBank(): could not find %s entry", bankName.c_str());
195 }
196 
open()197 int Player_HE::open() {
198 	if (_midi)
199 		return _midi->open();
200 	return 0;
201 }
202 
isOpen() const203 bool Player_HE::isOpen() const {
204 	if (_midi)
205 		return _midi->isOpen();
206 	return false;
207 }
208 
close()209 void Player_HE::close() {
210 	if (_midi)
211 		_midi->close();
212 }
213 
setTimerCallback(void * timerParam,Common::TimerManager::TimerProc timerProc)214 void Player_HE::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
215 	if (_midi)
216 		_midi->setTimerCallback(timerParam, timerProc);
217 }
218 
getBaseTempo()219 uint32 Player_HE::getBaseTempo() {
220 	if (_midi)
221 		return _midi->getBaseTempo();
222 	return 0;
223 }
224 
send(uint32 b)225 void Player_HE::send(uint32 b) {
226 	byte chan = b & 0x0f;
227 	byte cmd = b & 0xf0;
228 	byte op1 = (b >> 8) & 0x7f;
229 	byte op2 = (b >> 16) & 0x7f;
230 	if (cmd == 0xb0 && op1 == 0x07) {
231 		_channelVolume[chan] = op2;
232 		op2 = (op2 * _masterVolume) / 256;
233 		b = (b & 0xffff) | (op2 << 16);
234 	}
235 	if (_midi)
236 		_midi->send(b);
237 }
238 
239 }
240 
241 #endif
242