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 "common/stream.h"
24 #include "common/textconsole.h"
25
26 #include "gob/sound/musplayer.h"
27
28 namespace Gob {
29
MUSPlayer()30 MUSPlayer::MUSPlayer() : AdLib(60),
31 _songData(0), _songDataSize(0), _playPos(0), _songID(0) {
32
33 }
34
~MUSPlayer()35 MUSPlayer::~MUSPlayer() {
36 unload();
37 }
38
unload()39 void MUSPlayer::unload() {
40 stopPlay();
41
42 unloadSND();
43 unloadMUS();
44 }
45
skipToTiming()46 void MUSPlayer::skipToTiming() {
47 while (*_playPos < 0x80)
48 _playPos++;
49
50 if (*_playPos != 0xF8)
51 _playPos--;
52 }
53
pollMusic(bool first)54 uint32 MUSPlayer::pollMusic(bool first) {
55 if (_timbres.empty() || !_songData || !_playPos || (_playPos >= (_songData + _songDataSize))) {
56 end();
57 return 0;
58 }
59
60 if (first) {
61 // Set the timer frequency on first run.
62 // Do not set it in rewind() for thread safety reasons.
63 setTimerFrequency((_ticksPerBeat * _tempo) / 60);
64 return *_playPos++;
65 }
66
67 uint16 delay = 0;
68 while (delay == 0) {
69 byte cmd = *_playPos;
70
71 // Delay overflow
72 if (cmd == 0xF8) {
73 _playPos++;
74 delay = 0xF8;
75 break;
76 }
77
78 // Song end marker
79 if (cmd == 0xFC) {
80 end();
81 return 0;
82 }
83
84 // Global command
85 if (cmd == 0xF0) {
86 _playPos++;
87
88 byte type1 = *_playPos++;
89 byte type2 = *_playPos++;
90
91 if ((type1 == 0x7F) && (type2 == 0)) {
92 // Tempo change, as a fraction of the base tempo
93
94 uint32 num = *_playPos++;
95 uint32 denom = *_playPos++;
96
97 _tempo = _baseTempo * num + ((_baseTempo * denom) >> 7);
98 setTimerFrequency((_ticksPerBeat * _tempo) / 60);
99
100 _playPos++;
101 } else {
102
103 // Unsupported global command, skip it
104 _playPos -= 2;
105 while(*_playPos++ != 0xF7)
106 ;
107 }
108
109 delay = *_playPos++;
110 break;
111 }
112
113 // Voice command
114
115 if (cmd >= 0x80) {
116 _playPos++;
117
118 _lastCommand = cmd;
119 } else
120 cmd = _lastCommand;
121
122 uint8 voice = cmd & 0x0F;
123 uint8 note, volume;
124 uint16 pitch;
125
126 switch (cmd & 0xF0) {
127 case 0x80: // Note off
128 _playPos += 2;
129 noteOff(voice);
130 break;
131
132 case 0x90: // Note on
133 note = *_playPos++;
134 volume = *_playPos++;
135
136 if (volume) {
137 setVoiceVolume(voice, volume);
138 noteOn(voice, note);
139 } else
140 noteOff(voice);
141 break;
142
143 case 0xA0: // Set volume
144 setVoiceVolume(voice, *_playPos++);
145 break;
146
147 case 0xB0:
148 _playPos += 2;
149 break;
150
151 case 0xC0: // Set instrument
152 setInstrument(voice, *_playPos++);
153 break;
154
155 case 0xD0:
156 _playPos++;
157 break;
158
159 case 0xE0: // Pitch bend
160 pitch = *_playPos++;
161 pitch += *_playPos++ << 7;
162 bendVoicePitch(voice, pitch);
163 break;
164
165 default:
166 warning("MUSPlayer: Unsupported command: 0x%02X", cmd);
167 skipToTiming();
168 break;
169 }
170
171 delay = *_playPos++;
172 }
173
174 if (delay == 0xF8) {
175 delay = 240;
176
177 if (*_playPos != 0xF8)
178 delay += *_playPos++;
179 }
180
181 return delay;
182 }
183
rewind()184 void MUSPlayer::rewind() {
185 _playPos = _songData;
186 _tempo = _baseTempo;
187
188 _lastCommand = 0;
189
190 setPercussionMode(_soundMode != 0);
191 setPitchRange(_pitchBendRange);
192 }
193
loadSND(Common::SeekableReadStream & snd)194 bool MUSPlayer::loadSND(Common::SeekableReadStream &snd) {
195 unloadSND();
196
197 int timbreCount, timbrePos;
198 if (!readSNDHeader(snd, timbreCount, timbrePos))
199 return false;
200
201 if (!readSNDTimbres(snd, timbreCount, timbrePos) || snd.err()) {
202 unloadSND();
203 return false;
204 }
205
206 return true;
207 }
208
readString(Common::SeekableReadStream & stream,Common::String & string,byte * buffer,uint size)209 bool MUSPlayer::readString(Common::SeekableReadStream &stream, Common::String &string, byte *buffer, uint size) {
210 if (stream.read(buffer, size) != size)
211 return false;
212
213 buffer[size] = '\0';
214
215 string = (char *) buffer;
216
217 return true;
218 }
219
readSNDHeader(Common::SeekableReadStream & snd,int & timbreCount,int & timbrePos)220 bool MUSPlayer::readSNDHeader(Common::SeekableReadStream &snd, int &timbreCount, int &timbrePos) {
221 // Sanity check
222 if (snd.size() <= 6) {
223 warning("MUSPlayer::readSNDHeader(): File too small (%d)", snd.size());
224 return false;
225 }
226
227 // Version
228 const uint8 versionMajor = snd.readByte();
229 const uint8 versionMinor = snd.readByte();
230
231 if ((versionMajor != 1) && (versionMinor != 0)) {
232 warning("MUSPlayer::readSNDHeader(): Unsupported version %d.%d", versionMajor, versionMinor);
233 return false;
234 }
235
236 // Number of timbres and where they start
237 timbreCount = snd.readUint16LE();
238 timbrePos = snd.readUint16LE();
239
240 const uint16 minTimbrePos = 6 + timbreCount * 9;
241
242 // Sanity check
243 if (timbrePos < minTimbrePos) {
244 warning("MUSPlayer::readSNDHeader(): Timbre offset too small: %d < %d", timbrePos, minTimbrePos);
245 return false;
246 }
247
248 const uint32 timbreParametersSize = snd.size() - timbrePos;
249 const uint32 paramSize = kOperatorsPerVoice * kParamCount * sizeof(uint16);
250
251 // Sanity check
252 if (timbreParametersSize != (timbreCount * paramSize)) {
253 warning("MUSPlayer::loadSND(): Timbre parameters size mismatch: %d != %d",
254 timbreParametersSize, timbreCount * paramSize);
255 return false;
256 }
257
258 return true;
259 }
260
readSNDTimbres(Common::SeekableReadStream & snd,int timbreCount,int timbrePos)261 bool MUSPlayer::readSNDTimbres(Common::SeekableReadStream &snd, int timbreCount, int timbrePos) {
262 _timbres.resize(timbreCount);
263
264 // Read names
265 byte nameBuffer[10];
266 for (Common::Array<Timbre>::iterator t = _timbres.begin(); t != _timbres.end(); ++t) {
267 if (!readString(snd, t->name, nameBuffer, 9)) {
268 warning("MUSPlayer::readMUSTimbres(): Failed to read timbre name");
269 return false;
270 }
271 }
272
273 if (!snd.seek(timbrePos)) {
274 warning("MUSPlayer::readMUSTimbres(): Failed to seek to timbres");
275 return false;
276 }
277
278 // Read parameters
279 for (Common::Array<Timbre>::iterator t = _timbres.begin(); t != _timbres.end(); ++t) {
280 for (int i = 0; i < (kOperatorsPerVoice * kParamCount); i++)
281 t->params[i] = snd.readUint16LE();
282 }
283
284 return true;
285 }
286
loadMUS(Common::SeekableReadStream & mus)287 bool MUSPlayer::loadMUS(Common::SeekableReadStream &mus) {
288 unloadMUS();
289
290 if (!readMUSHeader(mus) || !readMUSSong(mus) || mus.err()) {
291 unloadMUS();
292 return false;
293 }
294
295 rewind();
296
297 return true;
298 }
299
readMUSHeader(Common::SeekableReadStream & mus)300 bool MUSPlayer::readMUSHeader(Common::SeekableReadStream &mus) {
301 // Sanity check
302 if (mus.size() <= 6)
303 return false;
304
305 // Version
306 const uint8 versionMajor = mus.readByte();
307 const uint8 versionMinor = mus.readByte();
308
309 if ((versionMajor != 1) && (versionMinor != 0)) {
310 warning("MUSPlayer::readMUSHeader(): Unsupported version %d.%d", versionMajor, versionMinor);
311 return false;
312 }
313
314 _songID = mus.readUint32LE();
315
316 byte nameBuffer[31];
317 if (!readString(mus, _songName, nameBuffer, 30)) {
318 warning("MUSPlayer::readMUSHeader(): Failed to read the song name");
319 return false;
320 }
321
322 _ticksPerBeat = mus.readByte();
323 _beatsPerMeasure = mus.readByte();
324
325 mus.skip(4); // Length of song in ticks
326
327 _songDataSize = mus.readUint32LE();
328
329 mus.skip(4); // Number of commands
330 mus.skip(8); // Unused
331
332 _soundMode = mus.readByte();
333 _pitchBendRange = mus.readByte();
334 _baseTempo = mus.readUint16LE();
335
336 mus.skip(8); // Unused
337
338 return true;
339 }
340
readMUSSong(Common::SeekableReadStream & mus)341 bool MUSPlayer::readMUSSong(Common::SeekableReadStream &mus) {
342 const uint32 realSongDataSize = mus.size() - mus.pos();
343
344 if (realSongDataSize < _songDataSize) {
345 warning("MUSPlayer::readMUSSong(): File too small for the song data: %d < %d", realSongDataSize, _songDataSize);
346 return false;
347 }
348
349 _songData = new byte[_songDataSize];
350
351 if (mus.read(_songData, _songDataSize) != _songDataSize) {
352 warning("MUSPlayer::readMUSSong(): Read failed");
353 return false;
354 }
355
356 return true;
357 }
358
unloadSND()359 void MUSPlayer::unloadSND() {
360 _timbres.clear();
361 }
362
unloadMUS()363 void MUSPlayer::unloadMUS() {
364 delete[] _songData;
365
366 _songData = 0;
367 _songDataSize = 0;
368
369 _playPos = 0;
370 }
371
getSongID() const372 uint32 MUSPlayer::getSongID() const {
373 return _songID;
374 }
375
getSongName() const376 const Common::String &MUSPlayer::getSongName() const {
377 return _songName;
378 }
379
setInstrument(uint8 voice,uint8 instrument)380 void MUSPlayer::setInstrument(uint8 voice, uint8 instrument) {
381 if (instrument >= _timbres.size())
382 return;
383
384 setVoiceTimbre(voice, _timbres[instrument].params);
385 }
386
387 } // End of namespace Gob
388