OpenTTD
midifile.cpp
1 /*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6 */
7 
8 /* @file midifile.cpp Parser for standard MIDI files */
9 
10 #include "midifile.hpp"
11 #include "../fileio_func.h"
12 #include "../fileio_type.h"
13 #include "../string_func.h"
14 #include "../core/endian_func.hpp"
15 #include "../base_media_base.h"
16 #include "midi.h"
17 #include <algorithm>
18 
19 #include "../console_func.h"
20 #include "../console_internal.h"
21 
22 /* SMF reader based on description at: http://www.somascape.org/midi/tech/mfile.html */
23 
24 
25 static MidiFile *_midifile_instance = nullptr;
26 
33 const byte *MidiGetStandardSysexMessage(MidiSysexMessage msg, size_t &length)
34 {
35  static byte reset_gm_sysex[] = { 0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7 };
36  static byte reset_gs_sysex[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7 };
37  static byte reset_xg_sysex[] = { 0xF0, 0x43, 0x10, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7 };
38  static byte roland_reverb_sysex[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x01, 0x30, 0x02, 0x04, 0x00, 0x40, 0x40, 0x00, 0x00, 0x09, 0xF7 };
39 
40  switch (msg) {
41  case MidiSysexMessage::ResetGM:
42  length = lengthof(reset_gm_sysex);
43  return reset_gm_sysex;
44  case MidiSysexMessage::ResetGS:
45  length = lengthof(reset_gs_sysex);
46  return reset_gs_sysex;
47  case MidiSysexMessage::ResetXG:
48  length = lengthof(reset_xg_sysex);
49  return reset_xg_sysex;
50  case MidiSysexMessage::RolandSetReverb:
51  length = lengthof(roland_reverb_sysex);
52  return roland_reverb_sysex;
53  default:
54  NOT_REACHED();
55  }
56 }
57 
62 class ByteBuffer {
63  byte *buf;
64  size_t buflen;
65  size_t pos;
66 public:
74  ByteBuffer(FILE *file, size_t len)
75  {
76  this->buf = MallocT<byte>(len);
77  if (fread(this->buf, 1, len, file) == len) {
78  this->buflen = len;
79  this->pos = 0;
80  } else {
81  /* invalid state */
82  this->buflen = 0;
83  }
84  }
85 
90  {
91  free(this->buf);
92  }
93 
98  bool IsValid() const
99  {
100  return this->buflen > 0;
101  }
102 
107  bool IsEnd() const
108  {
109  return this->pos >= this->buflen;
110  }
111 
117  bool ReadByte(byte &b)
118  {
119  if (this->IsEnd()) return false;
120  b = this->buf[this->pos++];
121  return true;
122  }
123 
131  bool ReadVariableLength(uint32 &res)
132  {
133  res = 0;
134  byte b = 0;
135  do {
136  if (this->IsEnd()) return false;
137  b = this->buf[this->pos++];
138  res = (res << 7) | (b & 0x7F);
139  } while (b & 0x80);
140  return true;
141  }
142 
149  bool ReadBuffer(byte *dest, size_t length)
150  {
151  if (this->IsEnd()) return false;
152  if (this->buflen - this->pos < length) return false;
153  memcpy(dest, this->buf + this->pos, length);
154  this->pos += length;
155  return true;
156  }
157 
164  bool ReadDataBlock(MidiFile::DataBlock *dest, size_t length)
165  {
166  if (this->IsEnd()) return false;
167  if (this->buflen - this->pos < length) return false;
168  dest->data.insert(dest->data.end(), this->buf + this->pos, this->buf + this->pos + length);
169  this->pos += length;
170  return true;
171  }
172 
178  bool Skip(size_t count)
179  {
180  if (this->IsEnd()) return false;
181  if (this->buflen - this->pos < count) return false;
182  this->pos += count;
183  return true;
184  }
185 
191  bool Rewind(size_t count)
192  {
193  if (count > this->pos) return false;
194  this->pos -= count;
195  return true;
196  }
197 };
198 
199 static bool ReadTrackChunk(FILE *file, MidiFile &target)
200 {
201  byte buf[4];
202 
203  const byte magic[] = { 'M', 'T', 'r', 'k' };
204  if (fread(buf, sizeof(magic), 1, file) != 1) {
205  return false;
206  }
207  if (memcmp(magic, buf, sizeof(magic)) != 0) {
208  return false;
209  }
210 
211  /* Read chunk length and then the whole chunk */
212  uint32 chunk_length;
213  if (fread(&chunk_length, 1, 4, file) != 4) {
214  return false;
215  }
216  chunk_length = FROM_BE32(chunk_length);
217 
218  ByteBuffer chunk(file, chunk_length);
219  if (!chunk.IsValid()) {
220  return false;
221  }
222 
223  target.blocks.push_back(MidiFile::DataBlock());
224  MidiFile::DataBlock *block = &target.blocks.back();
225 
226  byte last_status = 0;
227  bool running_sysex = false;
228  while (!chunk.IsEnd()) {
229  /* Read deltatime for event, start new block */
230  uint32 deltatime = 0;
231  if (!chunk.ReadVariableLength(deltatime)) {
232  return false;
233  }
234  if (deltatime > 0) {
235  target.blocks.push_back(MidiFile::DataBlock(block->ticktime + deltatime));
236  block = &target.blocks.back();
237  }
238 
239  /* Read status byte */
240  byte status;
241  if (!chunk.ReadByte(status)) {
242  return false;
243  }
244 
245  if ((status & 0x80) == 0) {
246  /* High bit not set means running status message, status is same as last
247  * convert to explicit status */
248  chunk.Rewind(1);
249  status = last_status;
250  goto running_status;
251  } else if ((status & 0xF0) != 0xF0) {
252  /* Regular channel message */
253  last_status = status;
254  running_status:
255  switch (status & 0xF0) {
256  case MIDIST_NOTEOFF:
257  case MIDIST_NOTEON:
258  case MIDIST_POLYPRESS:
259  case MIDIST_CONTROLLER:
260  case MIDIST_PITCHBEND:
261  /* 3 byte messages */
262  block->data.push_back(status);
263  if (!chunk.ReadDataBlock(block, 2)) {
264  return false;
265  }
266  break;
267  case MIDIST_PROGCHG:
268  case MIDIST_CHANPRESS:
269  /* 2 byte messages */
270  block->data.push_back(status);
271  if (!chunk.ReadByte(buf[0])) {
272  return false;
273  }
274  block->data.push_back(buf[0]);
275  break;
276  default:
277  NOT_REACHED();
278  }
279  } else if (status == MIDIST_SMF_META) {
280  /* Meta event, read event type byte and data length */
281  if (!chunk.ReadByte(buf[0])) {
282  return false;
283  }
284  uint32 length = 0;
285  if (!chunk.ReadVariableLength(length)) {
286  return false;
287  }
288  switch (buf[0]) {
289  case 0x2F:
290  /* end of track, no more data (length != 0 is illegal) */
291  return (length == 0);
292  case 0x51:
293  /* tempo change */
294  if (length != 3) return false;
295  if (!chunk.ReadBuffer(buf, 3)) return false;
296  target.tempos.push_back(MidiFile::TempoChange(block->ticktime, buf[0] << 16 | buf[1] << 8 | buf[2]));
297  break;
298  default:
299  /* unimportant meta event, skip over it */
300  if (!chunk.Skip(length)) {
301  return false;
302  }
303  break;
304  }
305  } else if (status == MIDIST_SYSEX || (status == MIDIST_SMF_ESCAPE && running_sysex)) {
306  /* System exclusive message */
307  uint32 length = 0;
308  if (!chunk.ReadVariableLength(length)) {
309  return false;
310  }
311  block->data.push_back(0xF0);
312  if (!chunk.ReadDataBlock(block, length)) {
313  return false;
314  }
315  if (block->data.back() != 0xF7) {
316  /* Engage Casio weirdo mode - convert to normal sysex */
317  running_sysex = true;
318  block->data.push_back(0xF7);
319  } else {
320  running_sysex = false;
321  }
322  } else if (status == MIDIST_SMF_ESCAPE) {
323  /* Escape sequence */
324  uint32 length = 0;
325  if (!chunk.ReadVariableLength(length)) {
326  return false;
327  }
328  if (!chunk.ReadDataBlock(block, length)) {
329  return false;
330  }
331  } else {
332  /* Messages undefined in standard midi files:
333  * 0xF1 - MIDI time code quarter frame
334  * 0xF2 - Song position pointer
335  * 0xF3 - Song select
336  * 0xF4 - undefined/reserved
337  * 0xF5 - undefined/reserved
338  * 0xF6 - Tune request for analog synths
339  * 0xF8..0xFE - System real-time messages
340  */
341  return false;
342  }
343  }
344 
345  NOT_REACHED();
346 }
347 
348 template<typename T>
349 bool TicktimeAscending(const T &a, const T &b)
350 {
351  return a.ticktime < b.ticktime;
352 }
353 
354 static bool FixupMidiData(MidiFile &target)
355 {
356  /* Sort all tempo changes and events */
357  std::sort(target.tempos.begin(), target.tempos.end(), TicktimeAscending<MidiFile::TempoChange>);
358  std::sort(target.blocks.begin(), target.blocks.end(), TicktimeAscending<MidiFile::DataBlock>);
359 
360  if (target.tempos.size() == 0) {
361  /* No tempo information, assume 120 bpm (500,000 microseconds per beat */
362  target.tempos.push_back(MidiFile::TempoChange(0, 500000));
363  }
364  /* Add sentinel tempo at end */
365  target.tempos.push_back(MidiFile::TempoChange(UINT32_MAX, 0));
366 
367  /* Merge blocks with identical tick times */
368  std::vector<MidiFile::DataBlock> merged_blocks;
369  uint32 last_ticktime = 0;
370  for (size_t i = 0; i < target.blocks.size(); i++) {
371  MidiFile::DataBlock &block = target.blocks[i];
372  if (block.data.size() == 0) {
373  continue;
374  } else if (block.ticktime > last_ticktime || merged_blocks.size() == 0) {
375  merged_blocks.push_back(block);
376  last_ticktime = block.ticktime;
377  } else {
378  merged_blocks.back().data.insert(merged_blocks.back().data.end(), block.data.begin(), block.data.end());
379  }
380  }
381  std::swap(merged_blocks, target.blocks);
382 
383  /* Annotate blocks with real time */
384  last_ticktime = 0;
385  uint32 last_realtime = 0;
386  size_t cur_tempo = 0, cur_block = 0;
387  while (cur_block < target.blocks.size()) {
388  MidiFile::DataBlock &block = target.blocks[cur_block];
389  MidiFile::TempoChange &tempo = target.tempos[cur_tempo];
390  MidiFile::TempoChange &next_tempo = target.tempos[cur_tempo+1];
391  if (block.ticktime <= next_tempo.ticktime) {
392  /* block is within the current tempo */
393  int64 tickdiff = block.ticktime - last_ticktime;
394  last_ticktime = block.ticktime;
395  last_realtime += uint32(tickdiff * tempo.tempo / target.tickdiv);
396  block.realtime = last_realtime;
397  cur_block++;
398  } else {
399  /* tempo change occurs before this block */
400  int64 tickdiff = next_tempo.ticktime - last_ticktime;
401  last_ticktime = next_tempo.ticktime;
402  last_realtime += uint32(tickdiff * tempo.tempo / target.tickdiv); // current tempo until the tempo change
403  cur_tempo++;
404  }
405  }
406 
407  return true;
408 }
409 
416 bool MidiFile::ReadSMFHeader(const char *filename, SMFHeader &header)
417 {
418  FILE *file = FioFOpenFile(filename, "rb", Subdirectory::BASESET_DIR);
419  if (!file) return false;
420  bool result = ReadSMFHeader(file, header);
421  FioFCloseFile(file);
422  return result;
423 }
424 
432 bool MidiFile::ReadSMFHeader(FILE *file, SMFHeader &header)
433 {
434  /* Try to read header, fixed size */
435  byte buffer[14];
436  if (fread(buffer, sizeof(buffer), 1, file) != 1) {
437  return false;
438  }
439 
440  /* Check magic, 'MThd' followed by 4 byte length indicator (always = 6 in SMF) */
441  const byte magic[] = { 'M', 'T', 'h', 'd', 0x00, 0x00, 0x00, 0x06 };
442  if (MemCmpT(buffer, magic, sizeof(magic)) != 0) {
443  return false;
444  }
445 
446  /* Read the parameters of the file */
447  header.format = (buffer[8] << 8) | buffer[9];
448  header.tracks = (buffer[10] << 8) | buffer[11];
449  header.tickdiv = (buffer[12] << 8) | buffer[13];
450  return true;
451 }
452 
458 bool MidiFile::LoadFile(const char *filename)
459 {
460  _midifile_instance = this;
461 
462  this->blocks.clear();
463  this->tempos.clear();
464  this->tickdiv = 0;
465 
466  bool success = false;
467  FILE *file = FioFOpenFile(filename, "rb", Subdirectory::BASESET_DIR);
468  if (file == nullptr) return false;
469 
470  SMFHeader header;
471  if (!ReadSMFHeader(file, header)) goto cleanup;
472 
473  /* Only format 0 (single-track) and format 1 (multi-track single-song) are accepted for now */
474  if (header.format != 0 && header.format != 1) goto cleanup;
475  /* Doesn't support SMPTE timecode files */
476  if ((header.tickdiv & 0x8000) != 0) goto cleanup;
477 
478  this->tickdiv = header.tickdiv;
479 
480  for (; header.tracks > 0; header.tracks--) {
481  if (!ReadTrackChunk(file, *this)) {
482  goto cleanup;
483  }
484  }
485 
486  success = FixupMidiData(*this);
487 
488 cleanup:
489  FioFCloseFile(file);
490  return success;
491 }
492 
493 
515 struct MpsMachine {
517  struct Channel {
518  byte cur_program;
520  uint16 delay;
521  uint32 playpos;
522  uint32 startpos;
523  uint32 returnpos;
524  Channel() : cur_program(0xFF), running_status(0), delay(0), playpos(0), startpos(0), returnpos(0) { }
525  };
526  Channel channels[16];
527  std::vector<uint32> segments;
528  int16 tempo_ticks;
532 
533  static const int TEMPO_RATE;
534  static const byte programvelocities[128];
535 
536  const byte *songdata;
537  size_t songdatalen;
539 
542  MPSMIDIST_SEGMENT_RETURN = 0xFD,
543  MPSMIDIST_SEGMENT_CALL = 0xFE,
544  MPSMIDIST_ENDSONG = 0xFF,
545  };
546 
547  static void AddMidiData(MidiFile::DataBlock &block, byte b1, byte b2)
548  {
549  block.data.push_back(b1);
550  block.data.push_back(b2);
551  }
552  static void AddMidiData(MidiFile::DataBlock &block, byte b1, byte b2, byte b3)
553  {
554  block.data.push_back(b1);
555  block.data.push_back(b2);
556  block.data.push_back(b3);
557  }
558 
565  MpsMachine(const byte *data, size_t length, MidiFile &target)
566  : songdata(data), songdatalen(length), target(target)
567  {
568  uint32 pos = 0;
569  int loopmax;
570  int loopidx;
571 
572  /* First byte is the initial "tempo" */
573  this->initial_tempo = this->songdata[pos++];
574 
575  /* Next byte is a count of callable segments */
576  loopmax = this->songdata[pos++];
577  for (loopidx = 0; loopidx < loopmax; loopidx++) {
578  /* Segments form a linked list in the stream,
579  * first two bytes in each is an offset to the next.
580  * Two bytes between offset to next and start of data
581  * are unaccounted for. */
582  this->segments.push_back(pos + 4);
583  pos += FROM_LE16(*(const int16 *)(this->songdata + pos));
584  }
585 
586  /* After segments follows list of master tracks for each channel,
587  * also prefixed with a byte counting actual tracks. */
588  loopmax = this->songdata[pos++];
589  for (loopidx = 0; loopidx < loopmax; loopidx++) {
590  /* Similar structure to segments list, but also has
591  * the MIDI channel number as a byte before the offset
592  * to next track. */
593  byte ch = this->songdata[pos++];
594  this->channels[ch].startpos = pos + 4;
595  pos += FROM_LE16(*(const int16 *)(this->songdata + pos));
596  }
597  }
598 
604  uint16 ReadVariableLength(uint32 &pos)
605  {
606  byte b = 0;
607  uint16 res = 0;
608  do {
609  b = this->songdata[pos++];
610  res = (res << 7) + (b & 0x7F);
611  } while (b & 0x80);
612  return res;
613  }
614 
618  void RestartSong()
619  {
620  for (int ch = 0; ch < 16; ch++) {
621  Channel &chandata = this->channels[ch];
622  if (chandata.startpos != 0) {
623  /* Active track, set position to beginning */
624  chandata.playpos = chandata.startpos;
625  chandata.delay = this->ReadVariableLength(chandata.playpos);
626  } else {
627  /* Inactive track, mark as such */
628  chandata.playpos = 0;
629  chandata.delay = 0;
630  }
631  }
632  }
633 
637  uint16 PlayChannelFrame(MidiFile::DataBlock &outblock, int channel)
638  {
639  uint16 newdelay = 0;
640  byte b1, b2;
641  Channel &chandata = this->channels[channel];
642 
643  do {
644  /* Read command/status byte */
645  b1 = this->songdata[chandata.playpos++];
646 
647  /* Command 0xFE, call segment from master track */
648  if (b1 == MPSMIDIST_SEGMENT_CALL) {
649  b1 = this->songdata[chandata.playpos++];
650  chandata.returnpos = chandata.playpos;
651  chandata.playpos = this->segments[b1];
652  newdelay = this->ReadVariableLength(chandata.playpos);
653  if (newdelay == 0) {
654  continue;
655  }
656  return newdelay;
657  }
658 
659  /* Command 0xFD, return from segment to master track */
660  if (b1 == MPSMIDIST_SEGMENT_RETURN) {
661  chandata.playpos = chandata.returnpos;
662  chandata.returnpos = 0;
663  newdelay = this->ReadVariableLength(chandata.playpos);
664  if (newdelay == 0) {
665  continue;
666  }
667  return newdelay;
668  }
669 
670  /* Command 0xFF, end of song */
671  if (b1 == MPSMIDIST_ENDSONG) {
672  this->shouldplayflag = false;
673  return 0;
674  }
675 
676  /* Regular MIDI channel message status byte */
677  if (b1 >= 0x80) {
678  /* Save the status byte as running status for the channel
679  * and read another byte for first parameter to command */
680  chandata.running_status = b1;
681  b1 = this->songdata[chandata.playpos++];
682  }
683 
684  switch (chandata.running_status & 0xF0) {
685  case MIDIST_NOTEOFF:
686  case MIDIST_NOTEON:
687  b2 = this->songdata[chandata.playpos++];
688  if (b2 != 0) {
689  /* Note on, read velocity and scale according to rules */
690  int16 velocity;
691  if (channel == 9) {
692  /* Percussion channel, fixed velocity scaling not in the table */
693  velocity = (int16)b2 * 0x50;
694  } else {
695  /* Regular channel, use scaling from table */
696  velocity = b2 * programvelocities[chandata.cur_program];
697  }
698  b2 = (velocity / 128) & 0x00FF;
699  AddMidiData(outblock, MIDIST_NOTEON + channel, b1, b2);
700  } else {
701  /* Note off */
702  AddMidiData(outblock, MIDIST_NOTEON + channel, b1, 0);
703  }
704  break;
705  case MIDIST_CONTROLLER:
706  b2 = this->songdata[chandata.playpos++];
707  if (b1 == MIDICT_MODE_MONO) {
708  /* Unknown what the purpose of this is.
709  * Occurs in "Can't get There from Here" and in "Aliens Ate my Railway" a few times each.
710  * Possibly intended to give hints to other (non-GM) music drivers decoding the song.
711  */
712  break;
713  } else if (b1 == 0) {
714  /* Standard MIDI controller 0 is "bank select", override meaning to change tempo.
715  * This is not actually used in any of the original songs. */
716  if (b2 != 0) {
717  this->current_tempo = ((int)b2) * 48 / 60;
718  }
719  break;
720  } else if (b1 == MIDICT_EFFECTS1) {
721  /* Override value of this controller, default mapping is Reverb Send Level according to MMA RP-023.
722  * Unknown what the purpose of this particular value is. */
723  b2 = 30;
724  }
725  AddMidiData(outblock, MIDIST_CONTROLLER + channel, b1, b2);
726  break;
727  case MIDIST_PROGCHG:
728  if (b1 == 0x7E) {
729  /* Program change to "Applause" is originally used
730  * to cause the song to loop, but that gets handled
731  * separately in the output driver here.
732  * Just end the song. */
733  this->shouldplayflag = false;
734  break;
735  }
736  /* Used for note velocity scaling lookup */
737  chandata.cur_program = b1;
738  /* Two programs translated to a third, this is likely to
739  * provide three different velocity scalings of "brass". */
740  if (b1 == 0x57 || b1 == 0x3F) {
741  b1 = 0x3E;
742  }
743  AddMidiData(outblock, MIDIST_PROGCHG + channel, b1);
744  break;
745  case MIDIST_PITCHBEND:
746  b2 = this->songdata[chandata.playpos++];
747  AddMidiData(outblock, MIDIST_PITCHBEND + channel, b1, b2);
748  break;
749  default:
750  break;
751  }
752 
753  newdelay = this->ReadVariableLength(chandata.playpos);
754  } while (newdelay == 0);
755 
756  return newdelay;
757  }
758 
763  {
764  /* Update tempo/ticks counter */
765  this->tempo_ticks -= this->current_tempo;
766  if (this->tempo_ticks > 0) {
767  return true;
768  }
769  this->tempo_ticks += TEMPO_RATE;
770 
771  /* Look over all channels, play those active */
772  for (int ch = 0; ch < 16; ch++) {
773  Channel &chandata = this->channels[ch];
774  if (chandata.playpos != 0) {
775  if (chandata.delay == 0) {
776  chandata.delay = this->PlayChannelFrame(block, ch);
777  }
778  chandata.delay--;
779  }
780  }
781 
782  return this->shouldplayflag;
783  }
784 
788  bool PlayInto()
789  {
790  /* Tempo seems to be handled as TEMPO_RATE = 148 ticks per second.
791  * Use this as the tickdiv, and define the tempo to be one second (1M microseconds) per tickdiv.
792  * MIDI software loading exported files will show a bogus tempo, but playback will be correct. */
793  this->target.tickdiv = TEMPO_RATE;
794  this->target.tempos.push_back(MidiFile::TempoChange(0, 1000000));
795 
796  /* Initialize playback simulation */
797  this->RestartSong();
798  this->shouldplayflag = true;
799  this->current_tempo = (int32)this->initial_tempo * 24 / 60;
800  this->tempo_ticks = this->current_tempo;
801 
802  /* Always reset percussion channel to program 0 */
803  this->target.blocks.push_back(MidiFile::DataBlock());
804  AddMidiData(this->target.blocks.back(), MIDIST_PROGCHG+9, 0x00);
805 
806  /* Technically should be an endless loop, but having
807  * a maximum (about 10 minutes) avoids getting stuck,
808  * in case of corrupted data. */
809  for (uint32 tick = 0; tick < 100000; tick+=1) {
810  this->target.blocks.push_back(MidiFile::DataBlock());
811  auto &block = this->target.blocks.back();
812  block.ticktime = tick;
813  if (!this->PlayFrame(block)) {
814  break;
815  }
816  }
817  return true;
818  }
819 };
821 const int MpsMachine::TEMPO_RATE = 148;
823 const byte MpsMachine::programvelocities[128] = {
824  100, 100, 100, 100, 100, 90, 100, 100, 100, 100, 100, 90, 100, 100, 100, 100,
825  100, 100, 85, 100, 100, 100, 100, 100, 100, 100, 100, 100, 90, 90, 110, 80,
826  100, 100, 100, 90, 70, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
827  100, 100, 90, 100, 100, 100, 100, 100, 100, 120, 100, 100, 100, 120, 100, 127,
828  100, 100, 90, 100, 100, 100, 100, 100, 100, 95, 100, 100, 100, 100, 100, 100,
829  100, 100, 100, 100, 100, 100, 100, 115, 100, 100, 100, 100, 100, 100, 100, 100,
830  100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
831  100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
832 };
833 
840 bool MidiFile::LoadMpsData(const byte *data, size_t length)
841 {
842  _midifile_instance = this;
843 
844  MpsMachine machine(data, length, *this);
845  return machine.PlayInto() && FixupMidiData(*this);
846 }
847 
848 bool MidiFile::LoadSong(const MusicSongInfo &song)
849 {
850  switch (song.filetype) {
851  case MTT_STANDARDMIDI:
852  return this->LoadFile(song.filename);
853  case MTT_MPSMIDI:
854  {
855  size_t songdatalen = 0;
856  byte *songdata = GetMusicCatEntryData(song.filename, song.cat_index, songdatalen);
857  if (songdata != nullptr) {
858  bool result = this->LoadMpsData(songdata, songdatalen);
859  free(songdata);
860  return result;
861  } else {
862  return false;
863  }
864  }
865  default:
866  NOT_REACHED();
867  }
868 }
869 
875 {
876  std::swap(this->blocks, other.blocks);
877  std::swap(this->tempos, other.tempos);
878  this->tickdiv = other.tickdiv;
879 
880  _midifile_instance = this;
881 
882  other.blocks.clear();
883  other.tempos.clear();
884  other.tickdiv = 0;
885 }
886 
887 static void WriteVariableLen(FILE *f, uint32 value)
888 {
889  if (value < 0x7F) {
890  byte tb = value;
891  fwrite(&tb, 1, 1, f);
892  } else if (value < 0x3FFF) {
893  byte tb[2];
894  tb[1] = value & 0x7F; value >>= 7;
895  tb[0] = (value & 0x7F) | 0x80; value >>= 7;
896  fwrite(tb, 1, sizeof(tb), f);
897  } else if (value < 0x1FFFFF) {
898  byte tb[3];
899  tb[2] = value & 0x7F; value >>= 7;
900  tb[1] = (value & 0x7F) | 0x80; value >>= 7;
901  tb[0] = (value & 0x7F) | 0x80; value >>= 7;
902  fwrite(tb, 1, sizeof(tb), f);
903  } else if (value < 0x0FFFFFFF) {
904  byte tb[4];
905  tb[3] = value & 0x7F; value >>= 7;
906  tb[2] = (value & 0x7F) | 0x80; value >>= 7;
907  tb[1] = (value & 0x7F) | 0x80; value >>= 7;
908  tb[0] = (value & 0x7F) | 0x80; value >>= 7;
909  fwrite(tb, 1, sizeof(tb), f);
910  }
911 }
912 
918 bool MidiFile::WriteSMF(const char *filename)
919 {
920  FILE *f = FioFOpenFile(filename, "wb", Subdirectory::NO_DIRECTORY);
921  if (!f) {
922  return false;
923  }
924 
925  /* SMF header */
926  const byte fileheader[] = {
927  'M', 'T', 'h', 'd', // block name
928  0x00, 0x00, 0x00, 0x06, // BE32 block length, always 6 bytes
929  0x00, 0x00, // writing format 0 (all in one track)
930  0x00, 0x01, // containing 1 track (BE16)
931  (byte)(this->tickdiv >> 8), (byte)this->tickdiv, // tickdiv in BE16
932  };
933  fwrite(fileheader, sizeof(fileheader), 1, f);
934 
935  /* Track header */
936  const byte trackheader[] = {
937  'M', 'T', 'r', 'k', // block name
938  0, 0, 0, 0, // BE32 block length, unknown at this time
939  };
940  fwrite(trackheader, sizeof(trackheader), 1, f);
941  /* Determine position to write the actual track block length at */
942  size_t tracksizepos = ftell(f) - 4;
943 
944  /* Write blocks in sequence */
945  uint32 lasttime = 0;
946  size_t nexttempoindex = 0;
947  for (size_t bi = 0; bi < this->blocks.size(); bi++) {
948  DataBlock &block = this->blocks[bi];
949  TempoChange &nexttempo = this->tempos[nexttempoindex];
950 
951  uint32 timediff = block.ticktime - lasttime;
952 
953  /* Check if there is a tempo change before this block */
954  if (nexttempo.ticktime < block.ticktime) {
955  timediff = nexttempo.ticktime - lasttime;
956  }
957 
958  /* Write delta time for block */
959  lasttime += timediff;
960  bool needtime = false;
961  WriteVariableLen(f, timediff);
962 
963  /* Write tempo change if there is one */
964  if (nexttempo.ticktime <= block.ticktime) {
965  byte tempobuf[6] = { MIDIST_SMF_META, 0x51, 0x03, 0, 0, 0 };
966  tempobuf[3] = (nexttempo.tempo & 0x00FF0000) >> 16;
967  tempobuf[4] = (nexttempo.tempo & 0x0000FF00) >> 8;
968  tempobuf[5] = (nexttempo.tempo & 0x000000FF);
969  fwrite(tempobuf, sizeof(tempobuf), 1, f);
970  nexttempoindex++;
971  needtime = true;
972  }
973  /* If a tempo change occurred between two blocks, rather than
974  * at start of this one, start over with delta time for the block. */
975  if (nexttempo.ticktime < block.ticktime) {
976  /* Start loop over at same index */
977  bi--;
978  continue;
979  }
980 
981  /* Write each block data command */
982  byte *dp = block.data.data();
983  while (dp < block.data.data() + block.data.size()) {
984  /* Always zero delta time inside blocks */
985  if (needtime) {
986  fputc(0, f);
987  }
988  needtime = true;
989 
990  /* Check message type and write appropriate number of bytes */
991  switch (*dp & 0xF0) {
992  case MIDIST_NOTEOFF:
993  case MIDIST_NOTEON:
994  case MIDIST_POLYPRESS:
995  case MIDIST_CONTROLLER:
996  case MIDIST_PITCHBEND:
997  fwrite(dp, 1, 3, f);
998  dp += 3;
999  continue;
1000  case MIDIST_PROGCHG:
1001  case MIDIST_CHANPRESS:
1002  fwrite(dp, 1, 2, f);
1003  dp += 2;
1004  continue;
1005  }
1006 
1007  /* Sysex needs to measure length and write that as well */
1008  if (*dp == MIDIST_SYSEX) {
1009  fwrite(dp, 1, 1, f);
1010  dp++;
1011  byte *sysexend = dp;
1012  while (*sysexend != MIDIST_ENDSYSEX) sysexend++;
1013  ptrdiff_t sysexlen = sysexend - dp;
1014  WriteVariableLen(f, sysexlen);
1015  fwrite(dp, 1, sysexend - dp, f);
1016  dp = sysexend;
1017  continue;
1018  }
1019 
1020  /* Fail for any other commands */
1021  fclose(f);
1022  return false;
1023  }
1024  }
1025 
1026  /* End of track marker */
1027  static const byte track_end_marker[] = { 0x00, MIDIST_SMF_META, 0x2F, 0x00 };
1028  fwrite(&track_end_marker, sizeof(track_end_marker), 1, f);
1029 
1030  /* Fill out the RIFF block length */
1031  size_t trackendpos = ftell(f);
1032  fseek(f, tracksizepos, SEEK_SET);
1033  uint32 tracksize = (uint32)(trackendpos - tracksizepos - 4); // blindly assume we never produce files larger than 2 GB
1034  tracksize = TO_BE32(tracksize);
1035  fwrite(&tracksize, 4, 1, f);
1036 
1037  fclose(f);
1038  return true;
1039 }
1040 
1048 std::string MidiFile::GetSMFFile(const MusicSongInfo &song)
1049 {
1050  if (song.filetype == MTT_STANDARDMIDI) {
1051  char filename[MAX_PATH];
1052  if (FioFindFullPath(filename, lastof(filename), Subdirectory::BASESET_DIR, song.filename)) {
1053  return std::string(filename);
1054  } else if (FioFindFullPath(filename, lastof(filename), Subdirectory::OLD_GM_DIR, song.filename)) {
1055  return std::string(filename);
1056  } else {
1057  return std::string();
1058  }
1059  }
1060 
1061  if (song.filetype != MTT_MPSMIDI) return std::string();
1062 
1063  char basename[MAX_PATH];
1064  {
1065  const char *fnstart = strrchr(song.filename, PATHSEPCHAR);
1066  if (fnstart == nullptr) {
1067  fnstart = song.filename;
1068  } else {
1069  fnstart++;
1070  }
1071 
1072  /* Remove all '.' characters from filename */
1073  char *wp = basename;
1074  for (const char *rp = fnstart; *rp != '\0'; rp++) {
1075  if (*rp != '.') *wp++ = *rp;
1076  }
1077  *wp++ = '\0';
1078  }
1079 
1080  char tempdirname[MAX_PATH];
1081  FioGetFullPath(tempdirname, lastof(tempdirname), Searchpath::SP_AUTODOWNLOAD_DIR, Subdirectory::BASESET_DIR, basename);
1082  if (!AppendPathSeparator(tempdirname, lastof(tempdirname))) return std::string();
1083  FioCreateDirectory(tempdirname);
1084 
1085  char output_filename[MAX_PATH];
1086  seprintf(output_filename, lastof(output_filename), "%s%d.mid", tempdirname, song.cat_index);
1087 
1088  if (FileExists(output_filename)) {
1089  /* If the file already exists, assume it's the correct decoded data */
1090  return std::string(output_filename);
1091  }
1092 
1093  byte *data;
1094  size_t datalen;
1095  data = GetMusicCatEntryData(song.filename, song.cat_index, datalen);
1096  if (data == nullptr) return std::string();
1097 
1098  MidiFile midifile;
1099  if (!midifile.LoadMpsData(data, datalen)) {
1100  free(data);
1101  return std::string();
1102  }
1103  free(data);
1104 
1105  if (midifile.WriteSMF(output_filename)) {
1106  return std::string(output_filename);
1107  } else {
1108  return std::string();
1109  }
1110 }
1111 
1112 
1113 static bool CmdDumpSMF(byte argc, char *argv[])
1114 {
1115  if (argc == 0) {
1116  IConsolePrint(CC_WARNING, "Write the current song to a Standard MIDI File. Usage: 'dumpsmf <filename>'");
1117  return true;
1118  }
1119  if (argc != 2) {
1120  IConsolePrint(CC_WARNING, "You must specify a filename to write MIDI data to.");
1121  return false;
1122  }
1123 
1124  if (_midifile_instance == nullptr) {
1125  IConsolePrint(CC_ERROR, "There is no MIDI file loaded currently, make sure music is playing, and you're using a driver that works with raw MIDI.");
1126  return false;
1127  }
1128 
1129  char fnbuf[MAX_PATH] = { 0 };
1130  if (seprintf(fnbuf, lastof(fnbuf), "%s%s", FiosGetScreenshotDir(), argv[1]) >= (int)lengthof(fnbuf)) {
1131  IConsolePrint(CC_ERROR, "Filename too long.");
1132  return false;
1133  }
1134  IConsolePrintF(CC_INFO, "Dumping MIDI to: %s", fnbuf);
1135 
1136  if (_midifile_instance->WriteSMF(fnbuf)) {
1137  IConsolePrint(CC_INFO, "File written successfully.");
1138  return true;
1139  } else {
1140  IConsolePrint(CC_ERROR, "An error occurred writing MIDI file.");
1141  return false;
1142  }
1143 }
1144 
1145 static void RegisterConsoleMidiCommands()
1146 {
1147  static bool registered = false;
1148  if (!registered) {
1149  IConsoleCmdRegister("dumpsmf", CmdDumpSMF);
1150  registered = true;
1151  }
1152 }
1153 
1154 MidiFile::MidiFile()
1155 {
1156  RegisterConsoleMidiCommands();
1157 }
1158 
1159 MidiFile::~MidiFile()
1160 {
1161  if (_midifile_instance == this) {
1162  _midifile_instance = nullptr;
1163  }
1164 }
1165 
uint32 startpos
start position of master track
Definition: midifile.cpp:522
Metadata about a music track.
Standard MIDI file.
bool PlayInto()
Perform playback of whole song.
Definition: midifile.cpp:788
bool IsEnd() const
Return whether reading has reached the end of the buffer.
Definition: midifile.cpp:107
Old subdirectory for the music.
Definition: fileio_type.h:114
Header of a Stanard MIDI File.
Definition: midi.h:16
bool LoadMpsData(const byte *data, size_t length)
Create MIDI data from song data for the original Microprose music drivers.
Definition: midifile.cpp:840
static int MemCmpT(const T *ptr1, const T *ptr2, size_t num=1)
Type-safe version of memcmp().
Definition: mem_func.hpp:63
Decoder for "MPS MIDI" format data.
Definition: midifile.cpp:515
Owning byte buffer readable as a stream.
Definition: midifile.cpp:62
bool LoadFile(const char *filename)
Load a standard MIDI file.
Definition: midifile.cpp:458
void FioFCloseFile(FILE *f)
Close a file in a safe way.
Definition: fileio.cpp:332
uint16 ReadVariableLength(uint32 &pos)
Read an SMF-style variable length value (note duration) from songdata.
Definition: midifile.cpp:604
byte * GetMusicCatEntryData(const char *filename, size_t entrynum, size_t &entrylen)
Read the full data of a music CAT file entry.
Definition: music.cpp:55
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
Definition: string.cpp:407
bool shouldplayflag
not-end-of-song flag
Definition: midifile.cpp:531
uint16 tickdiv
ticks per quarter note
Definition: midifile.hpp:36
#define lastof(x)
Get the last element of an fixed size array.
Definition: depend.cpp:48
void RestartSong()
Prepare for playback from the beginning.
Definition: midifile.cpp:618
void MoveFrom(MidiFile &other)
Move data from other to this, and clears other.
Definition: midifile.cpp:874
Subdirectory for all base data (base sets, intro game)
Definition: fileio_type.h:116
std::vector< DataBlock > blocks
sequential time-annotated data of file, merged to a single track
Definition: midifile.hpp:34
bool Rewind(size_t count)
Go a number of bytes back to re-read.
Definition: midifile.cpp:191
bool PlayFrame(MidiFile::DataBlock &block)
Play one frame of data into a block.
Definition: midifile.cpp:762
~ByteBuffer()
Destructor, frees the buffer.
Definition: midifile.cpp:89
byte running_status
last midi status code seen
Definition: midifile.cpp:519
bool AppendPathSeparator(char *buf, const char *last)
Appends, if necessary, the path separator character to the end of the string.
Definition: fileio.cpp:552
char * FioFindFullPath(char *buf, const char *last, Subdirectory subdir, const char *filename)
Find a path to the filename in one of the search directories.
Definition: fileio.cpp:354
void IConsolePrint(TextColour colour_code, const char *string)
Handle the printing of text entered into the console or redirected there by any other means...
Definition: console.cpp:84
Starting parameter and playback status for one channel/track.
Definition: midifile.cpp:517
FILE * FioFOpenFile(const char *filename, const char *mode, Subdirectory subdir, size_t *filesize)
Opens a OpenTTD file somewhere in a personal or global directory.
Definition: fileio.cpp:463
bool ReadDataBlock(MidiFile::DataBlock *dest, size_t length)
Read bytes into a MidiFile::DataBlock.
Definition: midifile.cpp:164
uint16 PlayChannelFrame(MidiFile::DataBlock &outblock, int channel)
Play one frame of data from one channel.
Definition: midifile.cpp:637
void CDECL IConsolePrintF(TextColour colour_code, const char *format,...)
Handle the printing of text entered into the console or redirected there by any other means...
Definition: console.cpp:124
A path without any base directory.
Definition: fileio_type.h:125
bool FileExists(const char *filename)
Test whether the given filename exists.
Definition: fileio.cpp:324
MPS GM driver MIDI format (contained in a CAT file)
std::vector< byte > data
raw midi data contained in block
Definition: midifile.hpp:25
bool ReadBuffer(byte *dest, size_t length)
Read bytes into a buffer.
Definition: midifile.cpp:149
static const byte programvelocities[128]
Base note velocities for various GM programs.
Definition: midifile.cpp:534
Search within the autodownload directory.
Definition: fileio_type.h:142
const char * filename
file on disk containing song (when used in MusicSet class, this pointer is owned by MD5File object fo...
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:40
uint32 tempo
new tempo in microseconds per tick
Definition: midifile.hpp:30
std::vector< TempoChange > tempos
list of tempo changes in file
Definition: midifile.hpp:35
uint32 realtime
real-time (microseconds) since start of file this block should be triggered at
Definition: midifile.hpp:24
uint32 returnpos
next return position after playing a segment
Definition: midifile.cpp:523
bool Skip(size_t count)
Skip over a number of bytes in the buffer.
Definition: midifile.cpp:178
bool ReadByte(byte &b)
Read a single byte from the buffer.
Definition: midifile.cpp:117
uint32 ticktime
tick number since start of file this tempo change occurs at
Definition: midifile.hpp:29
uint16 delay
frames until next command
Definition: midifile.cpp:520
static const int TEMPO_RATE
Frames/ticks per second for music playback.
Definition: midifile.cpp:533
int16 current_tempo
threshold for actually playing a frame
Definition: midifile.cpp:529
static std::string GetSMFFile(const MusicSongInfo &song)
Get the name of a Standard MIDI File for a given song.
Definition: midifile.cpp:1048
static bool ReadSMFHeader(const char *filename, SMFHeader &header)
Read the header of a standard MIDI file.
Definition: midifile.cpp:416
uint32 playpos
next byte to play this channel from
Definition: midifile.cpp:521
uint32 ticktime
tick number since start of file this block should be triggered at
Definition: midifile.hpp:23
MpsMachine(const byte *data, size_t length, MidiFile &target)
Construct a TTD DOS music format decoder.
Definition: midifile.cpp:565
int cat_index
entry index in CAT file, for filetype==MTT_MPSMIDI
static const TextColour CC_ERROR
Colour for error lines.
Definition: console_type.h:24
int16 tempo_ticks
ticker that increments when playing a frame, decrements before playing a frame
Definition: midifile.cpp:528
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:129
bool IsValid() const
Return whether the buffer was constructed successfully.
Definition: midifile.cpp:98
bool WriteSMF(const char *filename)
Write a Standard MIDI File containing the decoded music.
Definition: midifile.cpp:918
bool ReadVariableLength(uint32 &res)
Read a MIDI file variable length value.
Definition: midifile.cpp:131
const char * FiosGetScreenshotDir()
Get the directory for screenshots.
Definition: fios.cpp:643
byte cur_program
program selected, used for velocity scaling (lookup into programvelocities array) ...
Definition: midifile.cpp:518
MusicTrackType filetype
decoder required for song file
ByteBuffer(FILE *file, size_t len)
Construct buffer from data in a file.
Definition: midifile.cpp:74
void FioCreateDirectory(const char *name)
Create a directory with the given name.
Definition: fileio.cpp:532
MpsMidiStatus
Overridden MIDI status codes used in the data format.
Definition: midifile.cpp:541
const byte * songdata
raw data array
Definition: midifile.cpp:536
static const TextColour CC_WARNING
Colour for warning lines.
Definition: console_type.h:25
size_t songdatalen
length of song data
Definition: midifile.cpp:537
void IConsoleCmdRegister(const char *name, IConsoleCmdProc *proc, IConsoleHook *hook)
Register a new command to be used in the console.
Definition: console.cpp:248
MidiFile & target
recipient of data
Definition: midifile.cpp:538
int16 initial_tempo
starting tempo of song
Definition: midifile.cpp:530
static const TextColour CC_INFO
Colour for information lines.
Definition: console_type.h:26
std::vector< uint32 > segments
pointers into songdata to repeatable data segments
Definition: midifile.cpp:527