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