1 /*
2 ** music_hmi_midiout.cpp
3 ** Code to let ZDoom play HMI MIDI music through the MIDI streaming API.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2010 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 // HEADER FILES ------------------------------------------------------------
36 
37 #include <algorithm>
38 #include <assert.h>
39 #include "midisource.h"
40 #include "zmusic/zmusic_internal.h"
41 #include "zmusic/m_swap.h"
42 
43 // MACROS ------------------------------------------------------------------
44 
45 #define HMP_NEW_DATE	"013195"
46 #define HMI_SONG_MAGIC	"HMI-MIDISONG061595"
47 #define TRACK_MAGIC		"HMI-MIDITRACK"
48 
49 // Used by SendCommand to check for unexpected end-of-track conditions.
50 #define CHECK_FINISHED \
51 	if (track->TrackP >= track->MaxTrackP) \
52 	{ \
53 		track->Finished = true; \
54 		return events; \
55 	}
56 
57 // In song header
58 #define HMI_DIVISION_OFFSET			0xD4
59 #define HMI_TRACK_COUNT_OFFSET		0xE4
60 #define HMI_TRACK_DIR_PTR_OFFSET	0xE8
61 
62 #define HMP_DIVISION_OFFSET			0x38
63 #define HMP_TRACK_COUNT_OFFSET		0x30
64 #define HMP_DESIGNATIONS_OFFSET		0x94
65 #define HMP_TRACK_OFFSET_0			0x308	// original HMP
66 #define HMP_TRACK_OFFSET_1			0x388	// newer HMP
67 
68 // In track header
69 #define HMITRACK_DATA_PTR_OFFSET	0x57
70 #define HMITRACK_DESIGNATION_OFFSET	0x99
71 
72 #define HMPTRACK_LEN_OFFSET			4
73 #define HMPTRACK_DESIGNATION_OFFSET	8
74 #define HMPTRACK_MIDI_DATA_OFFSET	12
75 
76 #define NUM_HMP_DESIGNATIONS		5
77 #define NUM_HMI_DESIGNATIONS		8
78 
79 // MIDI device types for designation
80 #define HMI_DEV_GM					0xA000	// Generic General MIDI (not a real device)
81 #define HMI_DEV_MPU401				0xA001	// MPU-401, Roland Sound Canvas, Ensoniq SoundScape, Rolad RAP-10
82 #define HMI_DEV_OPL2				0xA002	// SoundBlaster (Pro), ESS AudioDrive
83 #define HMI_DEV_MT32				0xA004	// MT-32
84 #define HMI_DEV_SBAWE32				0xA008	// SoundBlaster AWE32
85 #define HMI_DEV_OPL3				0xA009	// SoundBlaster 16, Microsoft Sound System, Pro Audio Spectrum 16
86 #define HMI_DEV_GUS					0xA00A	// Gravis UltraSound, Gravis UltraSound Max/Ace
87 
88 // TYPES -------------------------------------------------------------------
89 
90 struct HMISong::TrackInfo
91 {
92 	const uint8_t *TrackBegin;
93 	size_t TrackP;
94 	size_t MaxTrackP;
95 	uint32_t Delay;
96 	uint32_t PlayedTime;
97 	uint16_t Designation[NUM_HMI_DESIGNATIONS];
98 	bool Enabled;
99 	bool Finished;
100 	uint8_t RunningStatus;
101 
102 	uint32_t ReadVarLenHMI();
103 	uint32_t ReadVarLenHMP();
104 };
105 
106 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
107 
108 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
109 
110 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
111 
112 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
113 
114 // PRIVATE DATA DEFINITIONS ------------------------------------------------
115 
116 // PUBLIC DATA DEFINITIONS -------------------------------------------------
117 
118 // CODE --------------------------------------------------------------------
119 
120 //==========================================================================
121 //
122 // HMISong Constructor
123 //
124 // Buffers the file and does some validation of the HMI header.
125 //
126 //==========================================================================
127 
HMISong(const uint8_t * data,size_t len)128 HMISong::HMISong (const uint8_t *data, size_t len)
129 {
130 	if (len < 0x100)
131 	{ // Way too small to be HMI.
132 		return;
133 	}
134 	MusHeader.resize(len);
135 	memcpy(MusHeader.data(), data, len);
136 	NumTracks = 0;
137 
138 	// Do some validation of the MIDI file
139 	if (memcmp(&MusHeader[0], HMI_SONG_MAGIC, sizeof(HMI_SONG_MAGIC)) == 0)
140 	{
141 		SetupForHMI((int)len);
142 	}
143 	else if (memcmp(&MusHeader[0], "HMIMIDIP", 8) == 0)
144 	{
145 		SetupForHMP((int)len);
146 	}
147 }
148 
149 //==========================================================================
150 //
151 // HMISong :: SetupForHMI
152 //
153 //==========================================================================
154 
SetupForHMI(int len)155 void HMISong::SetupForHMI(int len)
156 {
157 	int i, p;
158 
159 	auto MusPtr = &MusHeader[0];
160 
161 	ReadVarLen = ReadVarLenHMI;
162 	NumTracks = GetShort(MusPtr + HMI_TRACK_COUNT_OFFSET);
163 
164 	if (NumTracks <= 0)
165 	{
166 		return;
167 	}
168 
169 	// The division is the number of pulses per quarter note (PPQN).
170 	// HMI files have two values here, a full value and a quarter value. Some games,
171 	// notably Quarantines, have identical values for some reason, so it's safer to
172 	// use the quarter value and multiply it by four than to trust the full value.
173 	Division = GetShort(MusPtr + HMI_DIVISION_OFFSET) << 2;
174 	Tempo = InitialTempo = 4000000;
175 
176 	Tracks.resize(NumTracks + 1);
177 	int track_dir = GetInt(MusPtr + HMI_TRACK_DIR_PTR_OFFSET);
178 
179 	// Gather information about each track
180 	for (i = 0, p = 0; i < NumTracks; ++i)
181 	{
182 		int start = GetInt(MusPtr + track_dir + i*4);
183 		int tracklen, datastart;
184 
185 		if (start > len - HMITRACK_DESIGNATION_OFFSET - 4)
186 		{ // Track is incomplete.
187 			continue;
188 		}
189 
190 		// BTW, HMI does not actually check the track header.
191 		if (memcmp(MusPtr + start, TRACK_MAGIC, 13) != 0)
192 		{
193 			continue;
194 		}
195 
196 		// The track ends where the next one begins. If this is the
197 		// last track, then it ends at the end of the file.
198 		if (i == NumTracks - 1)
199 		{
200 			tracklen = len - start;
201 		}
202 		else
203 		{
204 			tracklen = GetInt(MusPtr + track_dir + i*4 + 4) - start;
205 		}
206 		// Clamp incomplete tracks to the end of the file.
207 		tracklen = std::min(tracklen, len - start);
208 		if (tracklen <= 0)
209 		{
210 			continue;
211 		}
212 
213 		// Offset to actual MIDI events.
214 		datastart = GetInt(MusPtr + start + HMITRACK_DATA_PTR_OFFSET);
215 		tracklen -= datastart;
216 		if (tracklen <= 0)
217 		{
218 			continue;
219 		}
220 
221 		// Store track information
222 		Tracks[p].TrackBegin = MusPtr + start + datastart;
223 		Tracks[p].TrackP = 0;
224 		Tracks[p].MaxTrackP = tracklen;
225 
226 		// Retrieve track designations. We can't check them yet, since we have not yet
227 		// connected to the MIDI device.
228 		for (int ii = 0; ii < NUM_HMI_DESIGNATIONS; ++ii)
229 		{
230 			Tracks[p].Designation[ii] = GetShort(MusPtr + start + HMITRACK_DESIGNATION_OFFSET + ii*2);
231 		}
232 
233 		p++;
234 	}
235 
236 	// In case there were fewer actual chunks in the file than the
237 	// header specified, update NumTracks with the current value of p.
238 	NumTracks = p;
239 }
240 
241 //==========================================================================
242 //
243 // HMISong :: SetupForHMP
244 //
245 //==========================================================================
246 
SetupForHMP(int len)247 void HMISong::SetupForHMP(int len)
248 {
249 	int track_data;
250 	int i, p;
251 
252 	auto MusPtr = &MusHeader[0];
253 
254 	ReadVarLen = ReadVarLenHMP;
255 	if (MusPtr[8] == 0)
256 	{
257 		track_data = HMP_TRACK_OFFSET_0;
258 	}
259 	else if (memcmp(MusPtr + 8, HMP_NEW_DATE, sizeof(HMP_NEW_DATE)) == 0)
260 	{
261 		track_data = HMP_TRACK_OFFSET_1;
262 	}
263 	else
264 	{ // unknown HMIMIDIP version
265 		return;
266 	}
267 
268 	NumTracks = GetInt(MusPtr + HMP_TRACK_COUNT_OFFSET);
269 
270 	if (NumTracks <= 0)
271 	{
272 		return;
273 	}
274 
275 	// The division is the number of pulses per quarter note (PPQN).
276 	Division = GetInt(MusPtr + HMP_DIVISION_OFFSET);
277 	Tempo = InitialTempo = 1000000;
278 
279 	Tracks.resize(NumTracks + 1);
280 
281 	// Gather information about each track
282 	for (i = 0, p = 0; i < NumTracks; ++i)
283 	{
284 		int start = track_data;
285 		int tracklen;
286 
287 		if (start > len - HMPTRACK_MIDI_DATA_OFFSET)
288 		{ // Track is incomplete.
289 			break;
290 		}
291 
292 		tracklen = GetInt(MusPtr + start + HMPTRACK_LEN_OFFSET);
293 		track_data += tracklen;
294 
295 		// Clamp incomplete tracks to the end of the file.
296 		tracklen = std::min(tracklen, len - start);
297 		if (tracklen <= 0)
298 		{
299 			continue;
300 		}
301 
302 		// Subtract track header size.
303 		tracklen -= HMPTRACK_MIDI_DATA_OFFSET;
304 		if (tracklen <= 0)
305 		{
306 			continue;
307 		}
308 
309 		// Store track information
310 		Tracks[p].TrackBegin = MusPtr + start + HMPTRACK_MIDI_DATA_OFFSET;
311 		Tracks[p].TrackP = 0;
312 		Tracks[p].MaxTrackP = tracklen;
313 
314 		// Retrieve track designations. We can't check them yet, since we have not yet
315 		// connected to the MIDI device.
316 #if 0
317 		// This is completely a guess based on knowledge of how designations work with
318 		// HMI files. Some songs contain nothing but zeroes for this data, so I'd rather
319 		// not go around using it without confirmation.
320 
321 		Printf("Track %d: %d %08x %d:  \034I", i, GetInt(MusPtr + start),
322 			GetInt(MusPtr + start + 4), GetInt(MusPtr + start + 8));
323 
324 		int designations = HMP_DESIGNATIONS_OFFSET +
325 			GetInt(MusPtr + start + HMPTRACK_DESIGNATION_OFFSET) * 4 * NUM_HMP_DESIGNATIONS;
326 		for (int ii = 0; ii < NUM_HMP_DESIGNATIONS; ++ii)
327 		{
328 			Printf(" %04x", GetInt(MusPtr + designations + ii*4));
329 		}
330 		Printf("\n");
331 #endif
332 		Tracks[p].Designation[0] = HMI_DEV_GM;
333 		Tracks[p].Designation[1] = HMI_DEV_GUS;
334 		Tracks[p].Designation[2] = HMI_DEV_OPL2;
335 		Tracks[p].Designation[3] = 0;
336 
337 		p++;
338 	}
339 
340 	// In case there were fewer actual chunks in the file than the
341 	// header specified, update NumTracks with the current value of p.
342 	NumTracks = p;
343 }
344 
345 //==========================================================================
346 //
347 // HMISong :: CheckCaps
348 //
349 // Check track designations and disable tracks that have not been
350 // designated for the device we will be playing on.
351 //
352 //==========================================================================
353 
CheckCaps(int tech)354 void HMISong::CheckCaps(int tech)
355 {
356 	// What's the equivalent HMI device for our technology?
357 	if (tech == MIDIDEV_FMSYNTH)
358 	{
359 		tech = HMI_DEV_OPL3;
360 	}
361 	else if (tech == MIDIDEV_MIDIPORT)
362 	{
363 		tech = HMI_DEV_MPU401;
364 	}
365 	else
366 	{ // Good enough? Or should we just say we're GM.
367 		tech = HMI_DEV_SBAWE32;
368 	}
369 
370 	for (int i = 0; i < NumTracks; ++i)
371 	{
372 		Tracks[i].Enabled = false;
373 		// Track designations are stored in a 0-terminated array.
374 		for (unsigned int j = 0; j < NUM_HMI_DESIGNATIONS && Tracks[i].Designation[j] != 0; ++j)
375 		{
376 			if (Tracks[i].Designation[j] == tech)
377 			{
378 				Tracks[i].Enabled = true;
379 			}
380 			// If a track is designated for device 0xA000, it will be played by a MIDI
381 			// driver for device types 0xA000, 0xA001, and 0xA008. Why this does not
382 			// include the GUS, I do not know.
383 			else if (Tracks[i].Designation[j] == HMI_DEV_GM)
384 			{
385 				Tracks[i].Enabled = (tech == HMI_DEV_MPU401 || tech == HMI_DEV_SBAWE32);
386 			}
387 			// If a track is designated for device 0xA002, it will be played by a MIDI
388 			// driver for device types 0xA002 or 0xA009.
389 			else if (Tracks[i].Designation[j] == HMI_DEV_OPL2)
390 			{
391 				Tracks[i].Enabled = (tech == HMI_DEV_OPL3);
392 			}
393 			// Any other designation must match the specific MIDI driver device number.
394 			// (Which we handled first above.)
395 
396 			if (Tracks[i].Enabled)
397 			{ // This track's been enabled, so we can stop checking other designations.
398 				break;
399 			}
400 		}
401 	}
402 }
403 
404 
405 //==========================================================================
406 //
407 // HMISong :: DoInitialSetup
408 //
409 // Sets the starting channel volumes.
410 //
411 //==========================================================================
412 
DoInitialSetup()413 void HMISong :: DoInitialSetup()
414 {
415 	for (int i = 0; i < 16; ++i)
416 	{
417 		ChannelVolumes[i] = 100;
418 	}
419 }
420 
421 //==========================================================================
422 //
423 // HMISong :: DoRestart
424 //
425 // Rewinds every track.
426 //
427 //==========================================================================
428 
DoRestart()429 void HMISong :: DoRestart()
430 {
431 	int i;
432 
433 	// Set initial state.
434 	FakeTrack = &Tracks[NumTracks];
435 	NoteOffs.clear();
436 	for (i = 0; i <= NumTracks; ++i)
437 	{
438 		Tracks[i].TrackP = 0;
439 		Tracks[i].Finished = false;
440 		Tracks[i].RunningStatus = 0;
441 		Tracks[i].PlayedTime = 0;
442 	}
443 	ProcessInitialMetaEvents ();
444 	for (i = 0; i < NumTracks; ++i)
445 	{
446 		Tracks[i].Delay = ReadVarLen(&Tracks[i]);
447 	}
448 	Tracks[i].Delay = 0;	// for the FakeTrack
449 	Tracks[i].Enabled = true;
450 	TrackDue = Tracks.data();
451 	TrackDue = FindNextDue();
452 }
453 
454 //==========================================================================
455 //
456 // HMISong :: CheckDone
457 //
458 //==========================================================================
459 
CheckDone()460 bool HMISong::CheckDone()
461 {
462 	return TrackDue == nullptr;
463 }
464 
465 //==========================================================================
466 //
467 // HMISong :: MakeEvents
468 //
469 // Copies MIDI events from the file and puts them into a MIDI stream
470 // buffer. Returns the new position in the buffer.
471 //
472 //==========================================================================
473 
MakeEvents(uint32_t * events,uint32_t * max_event_p,uint32_t max_time)474 uint32_t *HMISong::MakeEvents(uint32_t *events, uint32_t *max_event_p, uint32_t max_time)
475 {
476 	uint32_t *start_events;
477 	uint32_t tot_time = 0;
478 	uint32_t time = 0;
479 	uint32_t delay;
480 
481 	start_events = events;
482 	while (TrackDue && events < max_event_p && tot_time <= max_time)
483 	{
484 		// It's possible that this tick may be nothing but meta-events and
485 		// not generate any real events. Repeat this until we actually
486 		// get some output so we don't send an empty buffer to the MIDI
487 		// device.
488 		do
489 		{
490 			delay = TrackDue->Delay;
491 			time += delay;
492 			// Advance time for all tracks by the amount needed for the one up next.
493 			tot_time += delay * Tempo / Division;
494 			AdvanceTracks(delay);
495 			// Play all events for this tick.
496 			do
497 			{
498 				bool sysex_noroom = false;
499 				uint32_t *new_events = SendCommand(events, TrackDue, time, max_event_p - events, sysex_noroom);
500 				if (sysex_noroom)
501 				{
502 					return events;
503 				}
504 				TrackDue = FindNextDue();
505 				if (new_events != events)
506 				{
507 					time = 0;
508 				}
509 				events = new_events;
510 			}
511 			while (TrackDue && TrackDue->Delay == 0 && events < max_event_p);
512 		}
513 		while (start_events == events && TrackDue);
514 		time = 0;
515 	}
516 	return events;
517 }
518 
519 //==========================================================================
520 //
521 // HMISong :: AdvanceTracks
522 //
523 // Advances time for all tracks by the specified amount.
524 //
525 //==========================================================================
526 
AdvanceTracks(uint32_t time)527 void HMISong::AdvanceTracks(uint32_t time)
528 {
529 	for (int i = 0; i <= NumTracks; ++i)
530 	{
531 		if (Tracks[i].Enabled && !Tracks[i].Finished)
532 		{
533 			Tracks[i].Delay -= time;
534 			Tracks[i].PlayedTime += time;
535 		}
536 	}
537 	NoteOffs.AdvanceTime(time);
538 }
539 
540 //==========================================================================
541 //
542 // HMISong :: SendCommand
543 //
544 // Places a single MIDIEVENT in the event buffer.
545 //
546 //==========================================================================
547 
SendCommand(uint32_t * events,TrackInfo * track,uint32_t delay,ptrdiff_t room,bool & sysex_noroom)548 uint32_t *HMISong::SendCommand (uint32_t *events, TrackInfo *track, uint32_t delay, ptrdiff_t room, bool &sysex_noroom)
549 {
550 	uint32_t len;
551 	uint8_t event, data1 = 0, data2 = 0;
552 
553 	// If the next event comes from the fake track, pop an entry off the note-off queue.
554 	if (track == FakeTrack)
555 	{
556 		AutoNoteOff off;
557 		NoteOffs.Pop(off);
558 		events[0] = delay;
559 		events[1] = 0;
560 		events[2] = MIDI_NOTEON | off.Channel | (off.Key << 8);
561 		return events + 3;
562 	}
563 
564 	sysex_noroom = false;
565 	size_t start_p = track->TrackP;
566 
567 	CHECK_FINISHED
568 	event = track->TrackBegin[track->TrackP++];
569 	CHECK_FINISHED
570 
571 	// The actual event type will be filled in below. If it's not a NOP,
572 	// the events pointer will be advanced once the actual event is written.
573 	// Otherwise, we do it at the end of the function.
574 	events[0] = delay;
575 	events[1] = 0;
576 	events[2] = MEVENT_NOP << 24;
577 
578 	if (event != MIDI_SYSEX && event != MIDI_META && event != MIDI_SYSEXEND && event != 0xFe)
579 	{
580 		// Normal short message
581 		if ((event & 0xF0) == 0xF0)
582 		{
583 			if (MIDI_CommonLengths[event & 15] > 0)
584 			{
585 				data1 = track->TrackBegin[track->TrackP++];
586 				if (MIDI_CommonLengths[event & 15] > 1)
587 				{
588 					data2 = track->TrackBegin[track->TrackP++];
589 				}
590 			}
591 		}
592 		else if ((event & 0x80) == 0)
593 		{
594 			data1 = event;
595 			event = track->RunningStatus;
596 		}
597 		else
598 		{
599 			track->RunningStatus = event;
600 			data1 = track->TrackBegin[track->TrackP++];
601 		}
602 
603 		CHECK_FINISHED
604 
605 		if (MIDI_EventLengths[(event&0x70)>>4] == 2)
606 		{
607 			data2 = track->TrackBegin[track->TrackP++];
608 		}
609 
610 		// Monitor channel volume controller changes.
611 		if ((event & 0x70) == (MIDI_CTRLCHANGE & 0x70) && data1 == 7)
612 		{
613 			data2 = VolumeControllerChange(event & 15, data2);
614 		}
615 
616 		if (event != MIDI_META)
617 		{
618 			events[2] = event | (data1<<8) | (data2<<16);
619 		}
620 
621 		if (ReadVarLen == ReadVarLenHMI && (event & 0x70) == (MIDI_NOTEON & 0x70))
622 		{ // HMI note on events include the time until an implied note off event.
623 			NoteOffs.AddNoteOff(track->ReadVarLenHMI(), event & 0x0F, data1);
624 		}
625 	}
626 	else
627 	{
628 		// SysEx events could potentially not have enough room in the buffer...
629 		if (event == MIDI_SYSEX || event == MIDI_SYSEXEND)
630 		{
631 			len = ReadVarLen(track);
632 			if (len >= (MAX_MIDI_EVENTS-1)*3*4 || skipSysex)
633 			{ // This message will never fit. Throw it away.
634 				track->TrackP += len;
635 			}
636 			else if (len + 12 >= (size_t)room * 4)
637 			{ // Not enough room left in this buffer. Backup and wait for the next one.
638 				track->TrackP = start_p;
639 				sysex_noroom = true;
640 				return events;
641 			}
642 			else
643 			{
644 				uint8_t *msg = (uint8_t *)&events[3];
645 				if (event == MIDI_SYSEX)
646 				{ // Need to add the SysEx marker to the message.
647 					events[2] = (MEVENT_LONGMSG << 24) | (len + 1);
648 					*msg++ = MIDI_SYSEX;
649 				}
650 				else
651 				{
652 					events[2] = (MEVENT_LONGMSG << 24) | len;
653 				}
654 				memcpy(msg, &track->TrackBegin[track->TrackP], len);
655 				msg += len;
656 				// Must pad with 0
657 				while ((size_t)msg & 3)
658 				{
659 					*msg++ = 0;
660 				}
661 				track->TrackP += len;
662 			}
663 		}
664 		else if (event == MIDI_META)
665 		{
666 			// It's a meta-event
667 			event = track->TrackBegin[track->TrackP++];
668 			CHECK_FINISHED
669 			len = ReadVarLen(track);
670 			CHECK_FINISHED
671 
672 			if (track->TrackP + len <= track->MaxTrackP)
673 			{
674 				switch (event)
675 				{
676 				case MIDI_META_EOT:
677 					track->Finished = true;
678 					break;
679 
680 				case MIDI_META_TEMPO:
681 					Tempo =
682 						(track->TrackBegin[track->TrackP+0]<<16) |
683 						(track->TrackBegin[track->TrackP+1]<<8)  |
684 						(track->TrackBegin[track->TrackP+2]);
685 					events[0] = delay;
686 					events[1] = 0;
687 					events[2] = (MEVENT_TEMPO << 24) | Tempo;
688 					break;
689 				}
690 				track->TrackP += len;
691 				if (track->TrackP == track->MaxTrackP)
692 				{
693 					track->Finished = true;
694 				}
695 			}
696 			else
697 			{
698 				track->Finished = true;
699 			}
700 		}
701 		else if (event == 0xFE)
702 		{ // Skip unknown HMI events.
703 			event = track->TrackBegin[track->TrackP++];
704 			CHECK_FINISHED
705 			if (event == 0x13 || event == 0x15)
706 			{
707 				track->TrackP += 6;
708 			}
709 			else if (event == 0x12 || event == 0x14)
710 			{
711 				track->TrackP += 2;
712 			}
713 			else if (event == 0x10)
714 			{
715 				track->TrackP += 2;
716 				CHECK_FINISHED
717 				track->TrackP += track->TrackBegin[track->TrackP] + 5;
718 				CHECK_FINISHED
719 			}
720 			else
721 			{ // No idea.
722 				track->Finished = true;
723 			}
724 		}
725 	}
726 	if (!track->Finished)
727 	{
728 		track->Delay = ReadVarLen(track);
729 	}
730 	// Advance events pointer unless this is a non-delaying NOP.
731 	if (events[0] != 0 || MEVENT_EVENTTYPE(events[2]) != MEVENT_NOP)
732 	{
733 		if (MEVENT_EVENTTYPE(events[2]) == MEVENT_LONGMSG)
734 		{
735 			events += 3 + ((MEVENT_EVENTPARM(events[2]) + 3) >> 2);
736 		}
737 		else
738 		{
739 			events += 3;
740 		}
741 	}
742 	return events;
743 }
744 
745 //==========================================================================
746 //
747 // HMISong :: ProcessInitialMetaEvents
748 //
749 // Handle all the meta events at the start of each track.
750 //
751 //==========================================================================
752 
ProcessInitialMetaEvents()753 void HMISong::ProcessInitialMetaEvents ()
754 {
755 	TrackInfo *track;
756 	int i;
757 	uint8_t event;
758 	uint32_t len;
759 
760 	for (i = 0; i < NumTracks; ++i)
761 	{
762 		track = &Tracks[i];
763 		while (!track->Finished &&
764 				track->TrackP < track->MaxTrackP - 4 &&
765 				track->TrackBegin[track->TrackP] == 0 &&
766 				track->TrackBegin[track->TrackP+1] == 0xFF)
767 		{
768 			event = track->TrackBegin[track->TrackP+2];
769 			track->TrackP += 3;
770 			len = ReadVarLen(track);
771 			if (track->TrackP + len <= track->MaxTrackP)
772 			{
773 				switch (event)
774 				{
775 				case MIDI_META_EOT:
776 					track->Finished = true;
777 					break;
778 
779 				case MIDI_META_TEMPO:
780 					SetTempo(
781 						(track->TrackBegin[track->TrackP+0]<<16) |
782 						(track->TrackBegin[track->TrackP+1]<<8)  |
783 						(track->TrackBegin[track->TrackP+2])
784 					);
785 					break;
786 				}
787 			}
788 			track->TrackP += len;
789 		}
790 		if (track->TrackP >= track->MaxTrackP - 4)
791 		{
792 			track->Finished = true;
793 		}
794 	}
795 }
796 
797 //==========================================================================
798 //
799 // HMISong :: ReadVarLenHMI											static
800 //
801 //==========================================================================
802 
ReadVarLenHMI(TrackInfo * track)803 uint32_t HMISong::ReadVarLenHMI(TrackInfo *track)
804 {
805 	return track->ReadVarLenHMI();
806 }
807 
808 //==========================================================================
809 //
810 // HMISong :: ReadVarLenHMP											static
811 //
812 //==========================================================================
813 
ReadVarLenHMP(TrackInfo * track)814 uint32_t HMISong::ReadVarLenHMP(TrackInfo *track)
815 {
816 	return track->ReadVarLenHMP();
817 }
818 
819 //==========================================================================
820 //
821 // HMISong :: TrackInfo :: ReadVarLenHMI
822 //
823 // Reads a variable-length SMF number.
824 //
825 //==========================================================================
826 
ReadVarLenHMI()827 uint32_t HMISong::TrackInfo::ReadVarLenHMI()
828 {
829 	uint32_t time = 0, t = 0x80;
830 
831 	while ((t & 0x80) && TrackP < MaxTrackP)
832 	{
833 		t = TrackBegin[TrackP++];
834 		time = (time << 7) | (t & 127);
835 	}
836 	return time;
837 }
838 
839 //==========================================================================
840 //
841 // HMISong :: TrackInfo :: ReadVarLenHMP
842 //
843 // Reads a variable-length HMP number. This is similar to the standard SMF
844 // variable length number, except it's stored little-endian, and the high
845 // bit set means the number is done.
846 //
847 //==========================================================================
848 
ReadVarLenHMP()849 uint32_t HMISong::TrackInfo::ReadVarLenHMP()
850 {
851 	uint32_t time = 0;
852 	uint8_t t = 0;
853 	int off = 0;
854 
855 	while (!(t & 0x80) && TrackP < MaxTrackP)
856 	{
857 		t = TrackBegin[TrackP++];
858 		time |= (t & 127) << off;
859 		off += 7;
860 	}
861 	return time;
862 }
863 
864 //==========================================================================
865 //
866 // NoteOffQueue :: AddNoteOff
867 //
868 //==========================================================================
869 
AddNoteOff(uint32_t delay,uint8_t channel,uint8_t key)870 void NoteOffQueue::AddNoteOff(uint32_t delay, uint8_t channel, uint8_t key)
871 {
872 	uint32_t i = (uint32_t)size();
873 	resize(i + 1);
874 	while (i > 0 && (*this)[Parent(i)].Delay > delay)
875 	{
876 		(*this)[i] = (*this)[Parent(i)];
877 		i = Parent(i);
878 	}
879 	(*this)[i].Delay = delay;
880 	(*this)[i].Channel = channel;
881 	(*this)[i].Key = key;
882 }
883 
884 //==========================================================================
885 //
886 // NoteOffQueue :: Pop
887 //
888 //==========================================================================
889 
Pop(AutoNoteOff & item)890 bool NoteOffQueue::Pop(AutoNoteOff &item)
891 {
892 	if (size() > 0)
893 	{
894 		item = front();
895 		front() = back();
896 		pop_back();
897 		Heapify();
898 		return true;
899 	}
900 	return false;
901 }
902 
903 //==========================================================================
904 //
905 // NoteOffQueue :: AdvanceTime
906 //
907 //==========================================================================
908 
AdvanceTime(uint32_t time)909 void NoteOffQueue::AdvanceTime(uint32_t time)
910 {
911 	// Because the time is decreasing by the same amount for every entry,
912 	// the heap property is maintained.
913 	for (auto &item : *this)
914 	{
915 		assert(item.Delay >= time);
916 		item.Delay -= time;
917 	}
918 }
919 
920 //==========================================================================
921 //
922 // NoteOffQueue :: Heapify
923 //
924 //==========================================================================
925 
Heapify()926 void NoteOffQueue::Heapify()
927 {
928 	unsigned int i = 0;
929 	for (;;)
930 	{
931 		unsigned int l = Left(i);
932 		unsigned int r = Right(i);
933 		unsigned int smallest = i;
934 		if (l < (unsigned)size() && (*this)[l].Delay < (*this)[i].Delay)
935 		{
936 			smallest = l;
937 		}
938 		if (r < (unsigned)size() && (*this)[r].Delay < (*this)[smallest].Delay)
939 		{
940 			smallest = r;
941 		}
942 		if (smallest == i)
943 		{
944 			break;
945 		}
946 		std::swap((*this)[i], (*this)[smallest]);
947 		i = smallest;
948 	}
949 }
950 
951 //==========================================================================
952 //
953 // HMISong :: FindNextDue
954 //
955 // Scans every track for the next event to play. Returns nullptr if all events
956 // have been consumed.
957 //
958 //==========================================================================
959 
FindNextDue()960 HMISong::TrackInfo *HMISong::FindNextDue ()
961 {
962 	TrackInfo *track;
963 	uint32_t best;
964 	int i;
965 
966 	// Give precedence to whichever track last had events taken from it.
967 	if (TrackDue != FakeTrack && !TrackDue->Finished && TrackDue->Delay == 0)
968 	{
969 		return TrackDue;
970 	}
971 	if (TrackDue == FakeTrack && NoteOffs.size() != 0 && NoteOffs[0].Delay == 0)
972 	{
973 		FakeTrack->Delay = 0;
974 		return FakeTrack;
975 	}
976 
977 	// Check regular tracks.
978 	track = nullptr;
979 	best = 0xFFFFFFFF;
980 	for (i = 0; i < NumTracks; ++i)
981 	{
982 		if (Tracks[i].Enabled && !Tracks[i].Finished && Tracks[i].Delay < best)
983 		{
984 			best = Tracks[i].Delay;
985 			track = &Tracks[i];
986 		}
987 	}
988 	// Check automatic note-offs.
989 	if (NoteOffs.size() != 0 && NoteOffs[0].Delay <= best)
990 	{
991 		FakeTrack->Delay = NoteOffs[0].Delay;
992 		return FakeTrack;
993 	}
994 	return track;
995 }
996 
997