1 /*
2 * Copyright 2010-2014 OpenXcom Developers.
3 *
4 * This file is part of OpenXcom.
5 *
6 * OpenXcom is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OpenXcom is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OpenXcom. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "GMCat.h"
21 #include <vector>
22 #include "Music.h"
23
24 namespace OpenXcom
25 {
26
read_uint32_le(const unsigned char * p)27 static inline unsigned read_uint32_le (const unsigned char *p)
28 {
29 return ((unsigned) p[0]) + (((unsigned) p[1]) << 8)
30 + (((unsigned) p[2]) << 16) + (((unsigned) p[3]) << 24);
31 }
32
33 /// MIDI sequence.
34 struct seq {
35 unsigned size;
36 const unsigned char *data;
37 };
38
39 /// MIDI track.
40 struct track {
41 struct seq seq;
42 unsigned channel;
43 };
44
45 /// MIDI stream.
46 struct gmstream {
47 int tempo, nsubs, ntracks;
48 struct seq subs [256];
49 struct track tracks [256];
50 };
51
gmext_read_stream(struct gmstream * p,unsigned int n,const unsigned char * data)52 static int gmext_read_stream (struct gmstream *p,
53 unsigned int n, const unsigned char *data)
54 {
55 if (!n--)
56 return -1;
57 p->tempo = *data++;
58
59 // subsequences
60 if (!n--)
61 return -1;
62 p->nsubs = *data++;
63
64 for (int i=0; i<p->nsubs; ++i) {
65 if (n < 4)
66 return -1;
67 unsigned int s = read_uint32_le(data);
68 if (s < 4)
69 return -1;
70 p->subs[i].size = s - 4;
71 p->subs[i].data = data + 4;
72 n -= s;
73 data += s;
74 }
75
76 // tracks
77 if (!n--)
78 return -1;
79 p->ntracks = *data++;
80
81 for (int i=0; i<p->ntracks; ++i) {
82 if (n-- < 5)
83 return -1;
84 p->tracks[i].channel = *data++;
85 unsigned int s = read_uint32_le(data);
86 if (s < 4)
87 return -1;
88 p->tracks[i].seq.size = s - 4;
89 p->tracks[i].seq.data = data + 4;
90 n -= s;
91 data += s;
92 }
93
94 return n ? -1 : 0;
95 }
96
gmext_write_int16(std::vector<unsigned char> & midi,unsigned int n)97 static inline void gmext_write_int16 (std::vector<unsigned char> &midi,
98 unsigned int n)
99 {
100 midi.push_back(n >> 8);
101 midi.push_back(n);
102 }
103
gmext_write_delta(std::vector<unsigned char> & midi,unsigned int delta)104 static inline void gmext_write_delta (std::vector<unsigned char> &midi,
105 unsigned int delta)
106 {
107 unsigned char data[4];
108 unsigned int i = 0;
109
110 delta &= ((1<<28)-1);
111 do {
112 data[i++] = delta & 0x7F;
113 delta >>= 7;
114 } while (delta > 0);
115
116 while (--i)
117 midi.push_back(data[i] | 0x80);
118
119 midi.push_back(data[0]);
120 }
121
gmext_write_tempo_ev(std::vector<unsigned char> & midi,unsigned int tempo)122 static inline void gmext_write_tempo_ev (std::vector<unsigned char> &midi,
123 unsigned int tempo)
124 {
125 midi.push_back(0xFF);
126 midi.push_back(0x51);
127 midi.push_back(3);
128 tempo = 60000000 / tempo;
129 midi.push_back(tempo >> 16);
130 midi.push_back(tempo >> 8);
131 midi.push_back(tempo);
132 }
133
gmext_write_end_ev(std::vector<unsigned char> & midi)134 static inline void gmext_write_end_ev (std::vector<unsigned char> &midi)
135 {
136 midi.push_back(0xFF);
137 midi.push_back(0x2F);
138 midi.push_back(0);
139 }
140
141 static const unsigned int volume [0x80] = {
142 100,100,100,100,100, 90,100,100,100,100,100, 90,100,100,100,100,
143 100,100, 85,100,100,100,100,100,100,100,100,100, 90,90, 110, 80,
144 100,100,100, 90, 70,100,100,100,100,100,100,100,100,100,100,100,
145 100,100, 90,100,100,100,100,100,100,120,100,100,100,120,100,127,
146 100,100, 90,100,100,100,100,100,100, 95,100,100,100,100,100,100,
147 100,100,100,100,100,100,100,115,100,100,100,100,100,100,100,100,
148 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
149 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
150 };
151
152 /// Output status.
153 struct output_status {
154 unsigned int delta;
155 unsigned int patch;
156 unsigned char prevcmd;
157 };
158
gmext_write_sequence(std::vector<unsigned char> & midi,const struct gmstream * stream,unsigned int channel,const struct seq * seq,struct output_status * status)159 static int gmext_write_sequence (std::vector<unsigned char> &midi,
160 const struct gmstream *stream, unsigned int channel,
161 const struct seq *seq, struct output_status *status)
162 {
163 const unsigned char *data = seq->data;
164 unsigned int left = seq->size;
165
166 unsigned char cmd = -1;
167
168 while (left) {
169
170 // read delta
171 unsigned int ndelta = 0;
172
173 for (int i=0; ; ) {
174 unsigned char c = *data++;
175 left--;
176 ndelta += c & 0x7F;
177 if (!(c & 0x80))
178 break;
179 if ((++i == 4) || !left)
180 return -1;
181 ndelta <<= 7;
182 }
183
184 status->delta += ndelta;
185
186 // read cmd byte
187 if (!left)
188 return -1;
189
190 if (*data & 0x80) {
191 // actual cmd byte
192 cmd = *data++;
193 left--;
194 switch (cmd) {
195 case 0xFF: // end track
196 case 0xFD: // end subsequence
197 return 0;
198 case 0xFE: // insert subsequence
199 if (!left--)
200 return -1;
201 if (*data >= stream->nsubs)
202 // invalid subsequence
203 return -1;
204 if (gmext_write_sequence(midi,
205 stream, channel,
206 &stream->subs[*data++], status)
207 == -1)
208 return -1;
209 cmd = 0;
210 continue;
211 default:
212 cmd &= 0xF0;
213 }
214 } else if (cmd == 0)
215 return -1; // invalid running mode
216
217 if (!left--)
218 return -1;
219 unsigned char data1 = *data++;
220
221 switch (cmd) {
222
223 case 0x80:
224 case 0x90: {
225 if (!left--)
226 return -1;
227 unsigned char data2 = *data++;
228 if (data2)
229 data2 = (unsigned int) data2 *
230 (channel==9 ? 80 : volume[status->patch]) >> 7;
231 gmext_write_delta(midi, status->delta);
232 midi.push_back(cmd | channel);
233 midi.push_back(data1);
234 midi.push_back(data2);
235 } break;
236
237 case 0xC0:
238 if (data1 == 0x7E)
239 return 0; // restart stream
240 status->patch = data1;
241 if ((data1 == 0x57) || (data1 == 0x3F))
242 data1 = 0x3E;
243 gmext_write_delta(midi, status->delta);
244 midi.push_back(cmd | channel);
245 midi.push_back(data1);
246 break;
247
248 case 0xB0: {
249 if (!left--)
250 return -1;
251 unsigned char data2 = *data++;
252 if (data1 == 0x7E)
253 continue;
254 if (!data1) {
255 if (!data2)
256 continue;
257 gmext_write_delta(midi, status->delta);
258 gmext_write_tempo_ev(midi, 2*data2);
259 break;
260 }
261 if (data1 == 0x5B)
262 data2 = 0x1E;
263 gmext_write_delta(midi, status->delta);
264 midi.push_back(cmd | channel);
265 midi.push_back(data1);
266 midi.push_back(data2);
267 } break;
268
269 case 0xE0: {
270 if (!left--)
271 return -1;
272 unsigned char data2 = *data++;
273 gmext_write_delta(midi, status->delta);
274 midi.push_back(cmd | channel);
275 midi.push_back(data1);
276 midi.push_back(data2);
277 } break;
278
279 default: // unhandled cmd byte
280 return -1;
281 }
282
283 status->delta = 0;
284 }
285
286 return 0;
287 }
288
gmext_write_midi(const struct gmstream * stream,std::vector<unsigned char> & midi)289 static int gmext_write_midi (const struct gmstream *stream,
290 std::vector<unsigned char> &midi)
291 {
292 // write MIDI file header
293 static const unsigned char midi_file_signature[8] =
294 { 'M','T','h','d',0,0,0,6 };
295 for (int i=0; i<8; ++i)
296 midi.push_back(midi_file_signature[i]);
297 gmext_write_int16(midi, 1);
298 gmext_write_int16(midi, stream->ntracks + 1);
299 gmext_write_int16(midi, 24);
300
301 // write global tempo track
302 static const unsigned char midi_track_header[8] =
303 { 'M','T','r','k',0,0,0,11 };
304 for (int i=0; i<8; ++i)
305 midi.push_back(midi_track_header[i]);
306 gmext_write_delta(midi, 0);
307 gmext_write_tempo_ev(midi, stream->tempo);
308 gmext_write_delta(midi, 0);
309 gmext_write_end_ev(midi);
310
311 // write tracks
312 for (int j=0; j<stream->ntracks; ++j) {
313
314 // header
315 for (int i=0; i<4; ++i)
316 midi.push_back(midi_track_header[i]);
317
318 size_t loffset = midi.size();
319 for (int i=0; i<4; ++i)
320 midi.push_back(0);
321
322 // initial data
323 static const unsigned char midi_track_init[8] =
324 { /* 0, 0xB0, */ 0x78, 0, 0, 0x79, 0, 0, 0x7B, 0 };
325 midi.push_back(0);
326 midi.push_back(0xB0 | stream->tracks[j].channel);
327 for (int i=0; i<8; ++i)
328 midi.push_back(midi_track_init[i]);
329
330 // body
331 struct output_status status = { 0, 0, 0 };
332 if (gmext_write_sequence(midi, stream,
333 stream->tracks[j].channel,
334 &stream->tracks[j].seq, &status) == -1)
335 return -1;
336
337 // end of track
338 gmext_write_delta(midi, status.delta);
339 gmext_write_end_ev(midi);
340
341 // rewrite track length
342 unsigned char *p = &midi[loffset];
343 size_t length = midi.size() - loffset - 4;
344 p[0] = length >> 24;
345 p[1] = length >> 16;
346 p[2] = length >> 8;
347 p[3] = length;
348 }
349
350 return 0;
351 }
352
353 /**
354 * Loads a MIDI object into memory.
355 * @param i Music number to load.
356 * @return Pointer to the loaded music.
357 */
loadMIDI(unsigned int i)358 Music *GMCatFile::loadMIDI(unsigned int i)
359 {
360 Music *music = new Music;
361
362 unsigned char *raw = static_cast<unsigned char*> ((void*)load(i));
363
364 if (!raw)
365 return music;
366
367 // stream info
368 struct gmstream stream;
369 if (gmext_read_stream(&stream, getObjectSize(i), raw) == -1) {
370 delete[] raw;
371 return music;
372 }
373
374 std::vector<unsigned char> midi;
375 midi.reserve(65536);
376
377 // fields in stream still point into raw
378 if (gmext_write_midi(&stream, midi) == -1) {
379 delete[] raw;
380 return music;
381 }
382
383 delete[] raw;
384
385 music->load(&midi[0], midi.size());
386
387 return music;
388 }
389
390 }
391