Handle music script command 0x96 (script unconditional jump).
[scummvm-innocent.git] / engines / innocent / musicparser.cpp
blob5d87746a189a2f28242910202f2977f1b74f24ee
1 /* ScummVM - Graphic Adventure Engine
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.
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.
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.
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.
21 * $URL$
22 * $Id$
26 #include "innocent/musicparser.h"
28 #include "common/endian.h"
29 #include "sound/mididrv.h"
31 #include "innocent/resources.h"
32 #include "innocent/util.h"
34 namespace Innocent {
37 DECLARE_SINGLETON(MusicParser);
39 MusicParser::MusicParser() : MidiParser(), _time(0), _lasttick(0), _tick(0) {}
41 MusicParser::~MusicParser() {
42 silence();
43 unloadMusic();
44 // _driver->close(); // XXX segfaults
47 bool MusicParser::loadMusic(byte *data, uint32 /*size*/) {
48 unloadMusic();
49 silence();
50 _script.reset(new MusicScript(data));
51 _tune.reset(new Tune(_script->getTune()));
53 setTimerRate(_driver->getBaseTempo());
54 _driver->setTimerCallback(this, &MusicParser::timerCallback);
56 _num_tracks = 1;
57 _ppqn = 120;
58 // _clocks_per_tick = 0x19;
59 setTempo(500000 * 0x19);
60 setTrack(0);
61 return true;
64 void MusicParser::tick() {
65 _time += _timer_rate;
66 if (_lasttick && _time < _lasttick + _psec_per_tick)
67 return;
69 _lasttick = _time;
71 _tune->tick();
72 _tick++;
75 static byte notes[8][4];
77 enum {
78 kMidiNoteOff = 0x80,
79 kMidiNoteOn = 0x90,
80 kMidiChannelControl = 0xb0,
81 kMidiSetProgram = 0xc0
84 void MusicParser::silence() {
85 debugC(2, kDebugLevelMusic, "turning off all notes");
87 for (int channel = 2; channel < 10; channel++)
88 for (int i = 0; i < 4; i++)
89 if (notes[channel][i])
90 Music._driver->send(channel | kMidiNoteOff, notes[channel][i], 0);
92 memset(notes, 0, sizeof(notes));
95 MusicScript::MusicScript() : _code(0) {}
97 MusicScript::MusicScript(const byte *data) :
98 _code(data),
99 _offset(2) {}
101 enum {
102 kJump = 0x96,
103 kSetBeat = 0x9a,
104 kStop = 0x9b
107 void MusicScript::tick() {
108 while (true) {
109 switch (_code[_offset]) {
111 case kJump: {
112 uint16 target = READ_LE_UINT16(_code + _offset + 2);
113 debugC(2, kDebugLevelMusic, "will jump to music script at 0x%x", target);
114 _offset = target;
115 break;
118 case kSetBeat:
119 debugC(2, kDebugLevelMusic, "will set beat to %d", _code[_offset + 1]);
120 Music.setBeat(_code[_offset + 1]);
121 _offset += 2;
122 return;
124 case kStop:
125 debugC(2, kDebugLevelMusic, "will stop playing");
126 Music.silence();
127 Music.unloadMusic();
128 return;
130 default:
131 error("unhandled music script call %x", _code[_offset]);
136 Tune::Tune() : _currentBeat(-1) {}
138 enum {
139 kTuneBeatCountOffset = 0x21,
140 kTuneHeaderSize = 0x25
143 Tune::Tune(uint16 index) {
144 Res.loadTune(index, _data);
146 uint16 nbeats = READ_LE_UINT16(_data + kTuneBeatCountOffset);
147 _beats.resize(nbeats);
149 const byte *beat = _data + kTuneHeaderSize;
150 const byte *channels = beat + 8 * nbeats;
152 for (uint i = 0; i < _beats.size(); i++) {
153 debugC(2, kDebugLevelMusic, "found beat at offset 0x%x", beat - _data);
154 _beats[i] = Beat(beat, channels, _data);
155 beat += 8;
158 _currentBeat = 0;
159 _beatticks = 0;
162 void Tune::setBeat(uint16 index) {
163 _currentBeat = index;
164 _beats[_currentBeat].reset();
165 _beatticks = 0;
168 void Tune::tick() {
169 _beats[_currentBeat].tick();
170 _beatticks++;
171 if (_beatticks == 64)
172 setBeat(_currentBeat + 1);
173 _beatticks %= 64;
176 Beat::Beat() {}
178 Beat::Beat(const byte *def, const byte *channels, const byte *tune) {
179 for (int i = 0; i < 8; i++)
180 if (def[i]) {
181 uint16 off = 16 * def[i];
182 debugC(2, kDebugLevelMusic, "found channel at offset 0x%x", off + channels - tune);
183 _channels[i] = Channel(channels + off, tune, i + 2);
187 void Beat::reset(uint32 start) {
188 for (int i = 0; i < 8; i++)
189 _channels[i].reset();
192 void Beat::tick() {
193 for (int i = 0; i < 8; i++)
194 _channels[i].tick();
197 Channel::Channel() : _active(false) {}
199 Channel::Channel(const byte *def, const byte *tune, byte chanidx) {
200 for (int i = 0; i < 4; i++) {
201 const uint16 off = READ_LE_UINT16(def);
202 def += 2;
203 if (off) {
204 debugC(2, kDebugLevelMusic, "found note at offset 0x%x", off);
205 _notes[i] = Note(tune + off, i);
209 for (int i = 0; i < 4; i++) {
210 _init[i] = MusicCommand(def);
211 def += 2;
213 _active = true;
214 _not_initialized = true;
215 _initnote = 0;
216 _chanidx = chanidx;
219 void Channel::reset() {
220 unless (_active)
221 return;
223 _not_initialized = true;
224 _initnote = 0;
226 for (int i = 0; i < 4; i++)
227 _notes[i].reset();
230 enum {
231 kMidiCtrlExpression = 0xb,
232 kMidiCtrlAllNotesOff = 0x7b
235 void Channel::tick() {
236 if (_not_initialized) {
237 for (byte i = 0; i < 4; i++)
238 _init[i].exec(_chanidx);
239 _not_initialized = false;
242 for (byte i = 0; i < 4; i++)
243 _notes[i].tick(_chanidx);
246 Note::Note() : _data(0), _begin(0) {}
248 Note::Note(const byte *data, byte index) :
249 _data(data), _tick(0), _begin(data), _index(index) {}
251 void Note::setNote(byte n) {
252 notes[_channel - 2][_index] = n;
255 byte Note::note() const {
256 return notes[_channel - 2][_index];
259 void Note::reset() {
260 unless (_data)
261 return;
263 _tick = Music.getTick() + 1;
264 _data = _begin;
267 enum {
268 kSetTempo = 0x81,
269 kSetProgram = 0x82,
270 kCmdSetBeat = 0x85,
271 kSetExpression = 0x89,
272 kCmdNoteOff = 0x8b,
273 kCmdCallScript = 0x8c,
274 kHangNote = 0xfe
277 void Note::tick(byte channel) {
278 _channel = channel;
279 unless (_data && Music.getTick() == _tick)
280 return;
282 if (_data[0] == kHangNote) {
283 _tick += _data[1];
284 _data += 2;
285 return;
288 MusicCommand cmd(_data);
289 cmd.exec(channel, this);
291 _data += 2;
292 _tick++;
296 MusicCommand::MusicCommand() : _command(0) {}
298 bool MusicCommand::empty() const {
299 return _command == 0;
302 MusicCommand::MusicCommand(const byte *def) :
303 _command(def[0]),
304 _parameter(def[1]) {}
306 void MusicCommand::exec(byte channel, Note *note) {
307 unless (_command)
308 return;
310 switch (_command) {
312 case kSetProgram:
313 debugC(2, kDebugLevelMusic, "set program on channel %d to %d", channel, _parameter);
314 Music._driver->send(channel | kMidiSetProgram, MidiDriver::_mt32ToGm[_parameter], 0);
315 break;
317 case kSetExpression:
318 debugC(2, kDebugLevelMusic, "set expression on channel %d to %d", channel, _parameter);
319 Music._driver->send(channel | kMidiChannelControl, kMidiCtrlExpression, _parameter / 2);
320 break;
322 case kCmdNoteOff:
323 debugC(2, kDebugLevelMusic, "turn off note %d on channel %d", _parameter, channel);
325 assert(note);
326 Music._driver->send(channel | kMidiNoteOff, note->note(), 0);
327 note->setNote(0);
328 break;
330 case kCmdCallScript:
331 debugC(2, kDebugLevelMusic, "will call script");
332 Music._script->tick();
333 break;
335 case kSetTempo:
336 debugC(2, kDebugLevelMusic, "setting tempo to %d", _parameter);
337 Music.setTempo(500000 * _parameter);
338 break;
340 case kCmdSetBeat:
341 debugC(2, kDebugLevelMusic, "setting beat to %d", _parameter);
342 Music.setBeat(_parameter);
343 break;
345 default:
346 if (_command < 0x80) {
347 assert (note);
348 debugC(2, kDebugLevelMusic, "play note %d at volume %d on %d", _command, _parameter, channel);
350 if (note->note()) {
351 debugC(2, kDebugLevelMusic, "[first turn off note %d]", note->note());
353 Music._driver->send(channel | kMidiNoteOff, note->note(), 0);
356 Music._driver->send(channel | kMidiNoteOn, _command, _parameter);
357 note->setNote(_command);
358 break;
361 error("unhandled music command %x", _command);
365 } // End of namespace