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