Tempo done right.
[scummvm-innocent.git] / engines / innocent / musicparser.cpp
blobcda1298e88bfe114e583a1cfb30155ac9b889973
1 #include "innocent/musicparser.h"
3 #include "common/endian.h"
4 #include "sound/mididrv.h"
6 #include "innocent/resources.h"
7 #include "innocent/util.h"
9 namespace Innocent {
12 DECLARE_SINGLETON(MusicParser);
14 MusicParser::MusicParser() : MidiParser() {}
16 MusicParser::~MusicParser() {}
18 bool MusicParser::loadMusic(byte *data, uint32 /*size*/) {
19 _script = MusicScript(data);
21 _driver->open();
22 setTimerRate(_driver->getBaseTempo());
23 _driver->setTimerCallback(this, &MidiParser::timerCallback);
25 _num_tracks = 1;
26 _clocks_per_tick = 0x19;
27 setTrack(0);
28 return true;
31 void MusicParser::parseNextEvent(EventInfo &info) {
32 info.length = 0;
33 _script.parseNextEvent(info);
36 MusicScript::MusicScript() : _code(0) {}
38 MusicScript::MusicScript(const byte *data) :
39 _code(data),
40 _tune(READ_LE_UINT16(data)),
41 _offset(2) {}
43 enum {
44 kSetBeat = 0x9a
47 void MusicScript::parseNextEvent(EventInfo &info) {
48 MusicCommand::Status ret = _tune.parseNextEvent(info);
50 while (ret != MusicCommand::kThxBye) {
51 while (ret == MusicCommand::kCallMe) {
52 switch (_code[_offset]) {
54 case kSetBeat:
55 debugC(2, kDebugLevelMusic, "will set beat to %d", _code[_offset + 1]);
56 _tune.setBeat(_code[_offset + 1]);
57 _offset += 2;
58 ret = MusicCommand::kThxBye;
59 break;
61 default:
62 error("unhandled music script call %x", _code[_offset]);
65 ret = _tune.parseNextEvent(info);
69 Tune::Tune() : _currentBeat(-1) {}
71 enum {
72 kTuneBeatCountOffset = 0x21,
73 kTuneHeaderSize = 0x25
76 Tune::Tune(uint16 index) {
77 Res.loadTune(index, _data);
79 uint16 nbeats = READ_LE_UINT16(_data + kTuneBeatCountOffset);
80 _beats.resize(nbeats);
82 const byte *beat = _data + kTuneHeaderSize;
83 const byte *channels = beat + 8 * nbeats;
85 for (uint i = 0; i < _beats.size(); i++) {
86 _beats[i] = Beat(beat, channels, _data);
87 beat += 8;
90 _currentBeat = 0;
93 void Tune::setBeat(uint16 index) {
94 _currentBeat = index;
95 _beats[_currentBeat].reset();
98 MusicCommand::Status Tune::parseNextEvent(EventInfo &info) {
99 return _beats[_currentBeat].parseNextEvent(info);
102 Beat::Beat() {}
104 Beat::Beat(const byte *def, const byte *channels, const byte *tune) {
105 for (int i = 0; i < 8; i++)
106 if (def[i])
107 _channels[i] = Channel(channels + 16 * (def[i] - 1), tune, i + 2);
110 void Beat::reset() {
111 for (int i = 0; i < 8; i++)
112 _channels[i].reset();
115 MusicCommand::Status Beat::parseNextEvent(EventInfo &info) {
116 Channel *best = 0;
117 uint32 bestdelta = 0xffffffff;
118 for (int i = 0; i < 8; i++) {
119 uint32 delta = _channels[i].delta();
120 if (delta < bestdelta) {
121 bestdelta = delta;
122 best = &_channels[i];
126 return best->parseNextEvent(info);
129 Channel::Channel() : _active(false) {}
131 Channel::Channel(const byte *def, const byte *tune, byte chanidx) {
132 for (int i = 0; i < 4; i++) {
133 const uint16 off = READ_LE_UINT16(def);
134 def += 2;
135 if (off)
136 _notes[i] = Note(tune + off);
139 for (int i = 0; i < 4; i++) {
140 _init[i] = MusicCommand(def);
141 def += 2;
143 _active = true;
144 _not_initialized = true;
145 _initnote = 0;
146 _chanidx = chanidx;
149 void Channel::reset() {
150 unless (_active)
151 return;
153 _not_initialized = true;
154 _initnote = 0;
156 for (int i = 0; i < 4; i++)
157 _notes[i].reset();
160 uint32 Channel::delta() const {
161 unless (_active)
162 return 0xffffffff;
164 if (_not_initialized)
165 return 0;
167 uint32 bestdelta = 0xffffffff;
169 for (int i = 0; i < 4; i++) {
170 uint32 d = _notes[i].delta();
171 if (d < bestdelta)
172 bestdelta = d;
175 return bestdelta;
178 MusicCommand::Status Channel::parseNextEvent(EventInfo &info) {
179 MusicCommand::Status ret;
180 info.event = _chanidx;
181 info.delta = 0;
182 if (_not_initialized) {
183 while (_initnote < 4) {
184 unless (_init[_initnote].empty()) {
185 ret = _init[_initnote++].parseNextEvent(info);
186 break;
187 } else
188 _initnote++;
191 // let's see if we're finished with initialization
192 int i = _initnote;
193 while (i < 4)
194 unless (_init[i++].empty())
195 return ret;
197 _not_initialized = false;
198 } else {
199 uint32 bestdelta = 0xffffffff;
200 Note *best;
202 for (int i = 0; i < 4; i++) {
203 uint32 d = _notes[i].delta();
204 if (d < bestdelta) {
205 bestdelta = d;
206 best = &_notes[i];
210 info.event = _chanidx;
211 ret = best->parseNextEvent(info);
213 return ret;
217 Note::Note() : _data(0) {}
219 Note::Note(const byte *data) :
220 _data(data), _tick(0), _note(0), _begin(data) {}
222 void Note::reset() {
223 unless (_data)
224 return;
226 _tick = 0;
227 _note = 0;
228 _data = _begin;
231 uint32 Note::delta() const {
232 unless (_data)
233 return 0xffffffff;
234 if (_tick <= Music.getTick())
235 return 0;
236 else
237 return _tick - Music._position._last_event_tick;
240 enum {
241 kSetTempo = 0x81,
242 kSetProgram = 0x82,
243 kSetExpression = 0x89,
244 kCmdNoteOff = 0x8b,
245 kCmdCallScript = 0x8c,
246 kHangNote = 0xfe
249 enum {
250 kMidiNoteOff = 0x80,
251 kMidiNoteOn = 0x90,
252 kMidiChannelControl = 0xb0,
253 kMidiSetProgram = 0xc0
256 MusicCommand::Status Note::parseNextEvent(EventInfo &info) {
257 MusicCommand cmd(_data);
259 info.delta = delta();
260 info.basic.param1 = _note;
261 MusicCommand::Status ret = cmd.parseNextEvent(info);
262 if ((info.event & 0xf0) == kMidiNoteOn) {
263 if (_note) {
264 debugC(2, kDebugLevelMusic, "my note still playing, stopping it first");
265 info.event = kMidiNoteOff | (info.event & 0xf);
266 info.basic.param1 = _note;
267 _note = 0;
268 return MusicCommand::kThxBye;
270 _note = info.basic.param1;
273 _data += 2;
276 if (_data[0] == kHangNote) {
277 byte d = _data[1];
278 _data += 2;
279 unless (_tick)
280 _tick = Music.getTick();
281 _tick += d * Music.clocksPerTick();
283 return ret;
286 MusicCommand::MusicCommand() : _command(0) {}
288 bool MusicCommand::empty() const {
289 return _command == 0;
292 MusicCommand::MusicCommand(const byte *def) :
293 _command(def[0]),
294 _parameter(def[1]) {}
296 enum {
297 kMidiCtrlExpression = 0xb
300 MusicCommand::Status MusicCommand::parseNextEvent(EventInfo &info) {
301 switch (_command) {
303 case kSetProgram:
304 debugC(2, kDebugLevelMusic, "will set program on channel %d to %d in %d ticks", info.event, _parameter, info.delta);
305 info.event |= kMidiSetProgram;
306 info.basic.param1 = _parameter;
307 break;
309 case kSetExpression:
310 debugC(2, kDebugLevelMusic, "will set expression on channel %d to %d in %d ticks", info.event, _parameter, info.delta);
311 info.event |= kMidiChannelControl;
312 info.basic.param1 = kMidiCtrlExpression;
313 info.basic.param2 = _parameter;
314 break;
316 case kCmdNoteOff:
317 debugC(2, kDebugLevelMusic, "will turn off note %d on channel %d in %d ticks", info.basic.param1, info.event, info.delta);
318 info.event |= kMidiNoteOff;
319 break;
321 case kCmdCallScript:
322 debugC(2, kDebugLevelMusic, "will call script");
323 return kCallMe;
325 case kSetTempo:
326 debugC(2, kDebugLevelMusic, "setting tempo to %d", _parameter);
327 Music.setClocksPerTick(_parameter);
328 return kNvm;
330 default:
331 if (_command < 0x80) {
332 debugC(2, kDebugLevelMusic, "will play note %d at volume %d on %d in %d ticks", _command, _parameter, info.event, info.delta);
333 info.event |= kMidiNoteOn;
334 info.basic.param1 = _command;
335 info.basic.param2 = _parameter;
336 break;
339 error("unhandled music command %x", _command);
342 return kThxBye;
345 } // End of namespace