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