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