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.
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"
37 DECLARE_SINGLETON(MusicParser
);
39 MusicParser::MusicParser() : MidiParser(), _time(0), _lasttick(0), _tick(0) {}
41 MusicParser::~MusicParser() {
44 // _driver->close(); // XXX segfaults
47 bool MusicParser::loadMusic(byte
*data
, uint32
/*size*/) {
50 _script
.reset(new MusicScript(data
));
51 _tune
.reset(new Tune(_script
->getTune()));
53 setTimerRate(_driver
->getBaseTempo());
54 _driver
->setTimerCallback(this, &MusicParser::timerCallback
);
58 // _clocks_per_tick = 0x19;
59 setTempo(500000 * 0x19);
64 void MusicParser::tick() {
66 if (_lasttick
&& _time
< _lasttick
+ _psec_per_tick
)
75 static byte notes
[8][4];
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
) :
107 void MusicScript::tick() {
109 switch (_code
[_offset
]) {
112 uint16 target
= READ_LE_UINT16(_code
+ _offset
+ 2);
113 debugC(2, kDebugLevelMusic
, "will jump to music script at 0x%x", target
);
119 debugC(2, kDebugLevelMusic
, "will set beat to %d", _code
[_offset
+ 1]);
120 Music
.setBeat(_code
[_offset
+ 1]);
125 debugC(2, kDebugLevelMusic
, "will stop playing");
131 error("unhandled music script call %x", _code
[_offset
]);
136 Tune::Tune() : _currentBeat(-1) {}
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
);
162 void Tune::setBeat(uint16 index
) {
163 _currentBeat
= index
;
164 _beats
[_currentBeat
].reset();
169 _beats
[_currentBeat
].tick();
171 if (_beatticks
== 64)
172 setBeat(_currentBeat
+ 1);
178 Beat::Beat(const byte
*def
, const byte
*channels
, const byte
*tune
) {
179 for (int i
= 0; i
< 8; 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();
193 for (int i
= 0; i
< 8; i
++)
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
);
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
);
214 _not_initialized
= true;
219 void Channel::reset() {
223 _not_initialized
= true;
226 for (int i
= 0; i
< 4; i
++)
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
];
263 _tick
= Music
.getTick() + 1;
271 kSetExpression
= 0x89,
273 kCmdCallScript
= 0x8c,
277 void Note::tick(byte channel
) {
279 unless (_data
&& Music
.getTick() == _tick
)
282 if (_data
[0] == kHangNote
) {
288 MusicCommand
cmd(_data
);
289 cmd
.exec(channel
, this);
296 MusicCommand::MusicCommand() : _command(0) {}
298 bool MusicCommand::empty() const {
299 return _command
== 0;
302 MusicCommand::MusicCommand(const byte
*def
) :
304 _parameter(def
[1]) {}
306 void MusicCommand::exec(byte channel
, Note
*note
) {
313 debugC(2, kDebugLevelMusic
, "set program on channel %d to %d", channel
, _parameter
);
314 Music
._driver
->send(channel
| kMidiSetProgram
, MidiDriver::_mt32ToGm
[_parameter
], 0);
318 debugC(2, kDebugLevelMusic
, "set expression on channel %d to %d", channel
, _parameter
);
319 Music
._driver
->send(channel
| kMidiChannelControl
, kMidiCtrlExpression
, _parameter
/ 2);
323 debugC(2, kDebugLevelMusic
, "turn off note %d on channel %d", _parameter
, channel
);
326 Music
._driver
->send(channel
| kMidiNoteOff
, note
->note(), 0);
331 debugC(2, kDebugLevelMusic
, "will call script");
332 Music
._script
->tick();
336 debugC(2, kDebugLevelMusic
, "setting tempo to %d", _parameter
);
337 Music
.setTempo(500000 * _parameter
);
341 debugC(2, kDebugLevelMusic
, "setting beat to %d", _parameter
);
342 Music
.setBeat(_parameter
);
346 if (_command
< 0x80) {
348 debugC(2, kDebugLevelMusic
, "play note %d at volume %d on %d", _command
, _parameter
, channel
);
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
);
361 error("unhandled music command %x", _command
);
365 } // End of namespace