Calls script.
[scummvm-innocent.git] / engines / innocent / musicparser.cpp
blob42385dc6c1cf4e630df826d027626e85885d57fb
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 _ppqn = 64;
23 _driver->open();
24 setTimerRate(_driver->getBaseTempo());
25 _driver->setTimerCallback(this, &MidiParser::timerCallback);
27 setTempo(500000);
28 _num_tracks = 1;
29 setTrack(0);
30 return true;
33 void MusicParser::parseNextEvent(EventInfo &info) {
34 _script.parseNextEvent(info);
37 MusicScript::MusicScript() : _code(0) {}
39 MusicScript::MusicScript(const byte *data) :
40 _code(data),
41 _tune(READ_LE_UINT16(data)),
42 _offset(2) {}
44 void MusicScript::parseNextEvent(EventInfo &info) {
45 MusicCommand::Status ret = _tune.parseNextEvent(info);
47 while (ret != MusicCommand::kThxBye)
48 if (ret == MusicCommand::kCallMe) {
49 switch (_code[_offset]) {
50 default:
51 error("unhandled music script call %x", _code[_offset]);
53 } else
54 assert(false);
57 Tune::Tune() : _currentBeat(-1) {}
59 enum {
60 kTuneBeatCountOffset = 0x21,
61 kTuneHeaderSize = 0x25
64 Tune::Tune(uint16 index) {
65 index = 1;
66 Res.loadTune(index, _data);
68 uint16 nbeats = READ_LE_UINT16(_data + kTuneBeatCountOffset);
69 _beats.resize(nbeats);
71 const byte *beat = _data + kTuneHeaderSize;
72 const byte *channels = beat + 8 * nbeats;
74 for (uint i = 0; i < _beats.size(); i++) {
75 _beats[i] = Beat(beat, channels, _data);
76 beat += 8;
79 _currentBeat = 0;
82 MusicCommand::Status Tune::parseNextEvent(EventInfo &info) {
83 return _beats[_currentBeat].parseNextEvent(info);
86 Beat::Beat() {}
88 Beat::Beat(const byte *def, const byte *channels, const byte *tune) {
89 for (int i = 0; i < 8; i++)
90 if (def[i])
91 _channels[i] = Channel(channels + 16 * (def[i] - 1), tune, i + 2);
94 MusicCommand::Status Beat::parseNextEvent(EventInfo &info) {
95 Channel *best = 0;
96 uint32 bestdelta = 0xffffffff;
97 for (int i = 0; i < 8; i++) {
98 uint32 delta = _channels[i].delta();
99 if (delta < bestdelta) {
100 bestdelta = delta;
101 best = &_channels[i];
105 return best->parseNextEvent(info);
108 Channel::Channel() : _active(false) {}
110 Channel::Channel(const byte *def, const byte *tune, byte chanidx) {
111 for (int i = 0; i < 4; i++) {
112 const uint16 off = READ_LE_UINT16(def);
113 def += 2;
114 if (off)
115 _notes[i] = Note(tune + off);
118 for (int i = 0; i < 4; i++) {
119 _init[i] = MusicCommand(def);
120 def += 2;
122 _active = true;
123 _not_initialized = true;
124 _initnote = 0;
125 _chanidx = chanidx;
128 uint32 Channel::delta() const {
129 unless (_active)
130 return 0xffffffff;
132 if (_not_initialized)
133 return 0;
135 uint32 bestdelta = 0xffffffff;
137 for (int i = 0; i < 4; i++) {
138 uint32 d = _notes[i].delta();
139 if (d < bestdelta)
140 bestdelta = d;
143 return bestdelta;
146 MusicCommand::Status Channel::parseNextEvent(EventInfo &info) {
147 MusicCommand::Status ret;
148 info.event = _chanidx;
149 info.delta = 0;
150 if (_not_initialized) {
151 while (_initnote < 4) {
152 unless (_init[_initnote].empty()) {
153 ret = _init[_initnote++].parseNextEvent(info);
154 break;
155 } else
156 _initnote++;
159 // let's see if we're finished with initialization
160 int i = _initnote;
161 while (i < 4)
162 unless (_init[i++].empty())
163 return ret;
165 _not_initialized = false;
166 } else {
167 uint32 bestdelta = 0xffffffff;
168 Note *best;
170 for (int i = 0; i < 4; i++) {
171 uint32 d = _notes[i].delta();
172 if (d < bestdelta) {
173 bestdelta = d;
174 best = &_notes[i];
178 info.event = _chanidx;
179 ret = best->parseNextEvent(info);
181 return ret;
185 Note::Note() : _data(0) {}
187 Note::Note(const byte *data) :
188 _data(data), _tick(0), _note(0) {}
190 uint32 Note::delta() const {
191 unless (_data)
192 return 0xffffffff;
193 if (_tick <= Music.getTick())
194 return 0;
195 else
196 return _tick - Music._position._last_event_tick;
199 enum {
200 kSetProgram = 0x82,
201 kSetExpression = 0x89,
202 kCmdNoteOff = 0x8b,
203 kCmdCallScript = 0x8c,
204 kHangNote = 0xfe
207 enum {
208 kMidiNoteOff = 0x80,
209 kMidiNoteOn = 0x90,
210 kMidiChannelControl = 0xb0,
211 kMidiSetProgram = 0xc0
214 MusicCommand::Status Note::parseNextEvent(EventInfo &info) {
215 MusicCommand cmd(_data);
217 info.delta = delta();
218 info.basic.param1 = _note;
219 MusicCommand::Status ret = cmd.parseNextEvent(info);
220 if (info.event & 0xf0 == kMidiNoteOn) {
221 if (_note) {
222 info.event = kMidiNoteOff | (info.event & 0xf);
223 info.basic.param1 = _note;
224 _note = 0;
225 return MusicCommand::kThxBye;
227 _note = info.basic.param1;
230 _data += 2;
233 if (_data[0] == kHangNote) {
234 byte d = _data[1];
235 _data += 2;
236 unless (_tick)
237 _tick = Music.getTick();
238 _tick += d;
240 return ret;
243 MusicCommand::MusicCommand() : _command(0) {}
245 bool MusicCommand::empty() const {
246 return _command == 0;
249 MusicCommand::MusicCommand(const byte *def) :
250 _command(def[0]),
251 _parameter(def[1]) {}
253 enum {
254 kMidiCtrlExpression = 0xb
257 MusicCommand::Status MusicCommand::parseNextEvent(EventInfo &info) {
258 switch (_command) {
260 case kSetProgram:
261 debugC(2, kDebugLevelMusic, "will set program on channel %d to %d in %d ticks", info.event, _parameter, info.delta);
262 info.event |= kMidiSetProgram;
263 info.basic.param1 = _parameter;
264 break;
266 case kSetExpression:
267 debugC(2, kDebugLevelMusic, "will set expression on channel %d to %d in %d ticks", info.event, _parameter, info.delta);
268 info.event |= kMidiChannelControl;
269 info.basic.param1 = kMidiCtrlExpression;
270 info.basic.param2 = _parameter;
271 break;
273 case kCmdNoteOff:
274 debugC(2, kDebugLevelMusic, "will turn off note %d on channel %d in %d ticks", info.basic.param1, info.event, info.delta);
275 info.event |= kMidiNoteOff;
276 break;
278 case kCmdCallScript:
279 debugC(2, kDebugLevelMusic, "will call script");
280 return kCallMe;
282 default:
283 if (_command < 0x80) {
284 debugC(2, kDebugLevelMusic, "will play note %d at volume %d on %d in %d ticks", _command, _parameter, info.event, info.delta);
285 info.event |= kMidiNoteOn;
286 info.basic.param1 = _command;
287 info.basic.param2 = _parameter;
288 break;
291 error("unhandled music command %x", _command);
294 return kThxBye;
297 } // End of namespace