/ source / audiolib / src / midi.cpp
midi.cpp
   1  //-------------------------------------------------------------------------
   2  /*
   3  Copyright (C) 2010-2019 EDuke32 developers and contributors
   4  Copyright (C) 2019 Nuke.YKT
   5  
   6  This file is part of NBlood.
   7  
   8  NBlood is free software; you can redistribute it and/or
   9  modify it under the terms of the GNU General Public License version 2
  10  as published by the Free Software Foundation.
  11  
  12  This program is distributed in the hope that it will be useful,
  13  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15  
  16  See the GNU General Public License for more details.
  17  
  18  You should have received a copy of the GNU General Public License
  19  along with this program; if not, write to the Free Software
  20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  21  */
  22  //-------------------------------------------------------------------------
  23  
  24  /**********************************************************************
  25     module: MIDI.C
  26  
  27     author: James R. Dose
  28     date:   May 25, 1994
  29  
  30     Midi song file playback routines.
  31  
  32     (c) Copyright 1994 James R. Dose.  All Rights Reserved.
  33  **********************************************************************/
  34  
  35  #include "midi.h"
  36  
  37  #include "_midi.h"
  38  #include "compat.h"
  39  #include "drivers.h"
  40  #include "music.h"
  41  #include "pragmas.h"
  42  #include "sndcards.h"
  43  
  44  extern int MV_MixRate;
  45  extern int ASS_MIDISoundDriver;
  46  
  47  int MIDI_GetDevice()
  48  {
  49      return ASS_MIDISoundDriver;
  50  }
  51  
  52  static const int _MIDI_CommandLengths[NUM_MIDI_CHANNELS] = { 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0 };
  53  
  54  static track * _MIDI_TrackPtr;
  55  static int _MIDI_TrackMemSize;
  56  static int _MIDI_NumTracks;
  57  
  58  static int _MIDI_SongActive;
  59  static int _MIDI_SongLoaded;
  60  static int _MIDI_Loop;
  61  
  62  static int  _MIDI_Division;
  63  static int  _MIDI_Tick;
  64  static int  _MIDI_Beat    = 1;
  65  static int  _MIDI_Measure = 1;
  66  static uint32_t _MIDI_Time;
  67  static int  _MIDI_BeatsPerMeasure;
  68  static int  _MIDI_TicksPerBeat;
  69  static int  _MIDI_TimeBase;
  70  static int  _MIDI_FPSecondsPerTick;
  71  static uint32_t _MIDI_TotalTime;
  72  static int  _MIDI_TotalTicks;
  73  static int  _MIDI_TotalBeats;
  74  static int  _MIDI_TotalMeasures;
  75  
  76  uint32_t _MIDI_PositionInTicks;
  77  uint32_t _MIDI_GlobalPositionInTicks;
  78  
  79  static int  _MIDI_Context;
  80  
  81  static int _MIDI_ActiveTracks;
  82  static int _MIDI_TotalVolume = MIDI_MaxVolume;
  83  
  84  static int _MIDI_ChannelVolume[NUM_MIDI_CHANNELS];
  85  
  86  static midifuncs *_MIDI_Funcs;
  87  
  88  static int _MIDI_Reset;
  89  
  90  int MV_MIDIRenderTempo = -1;
  91  int MV_MIDIRenderTimer;
  92  
  93  static char *_MIDI_SongPtr;
  94  
  95  static void _MIDI_SetChannelVolume(int channel, int volume);
  96  
  97  void MIDI_Restart(void)
  98  {
  99      if (_MIDI_SongLoaded)
 100      {
 101          songposition pos;
 102          MIDI_GetSongPosition(&pos);
 103          MIDI_PlaySong(_MIDI_SongPtr, _MIDI_Loop);
 104          MIDI_SetSongPosition(pos.measure, pos.beat, pos.tick);
 105      }
 106  }
 107  
 108  static int _MIDI_ReadNumber(void *from, size_t size)
 109  {
 110      if (size > 4)
 111          size = 4;
 112  
 113      char *FromPtr = (char *)from;
 114      int value = 0;
 115  
 116      while (size--)
 117      {
 118          value <<= 8;
 119          value += *FromPtr++;
 120      }
 121  
 122      return value;
 123  }
 124  
 125  static int _MIDI_ReadDelta(track *ptr)
 126  {
 127      int value;
 128  
 129      GET_NEXT_EVENT(ptr, value);
 130  
 131      if (value & 0x80)
 132      {
 133          value &= 0x7f;
 134          char c;
 135  
 136          do
 137          {
 138              GET_NEXT_EVENT(ptr, c);
 139              value = (value << 7) + (c & 0x7f);
 140          }
 141          while (c & 0x80);
 142      }
 143  
 144      return value;
 145  }
 146  
 147  static void _MIDI_ResetTracks(void)
 148  {
 149      _MIDI_Tick = 0;
 150      _MIDI_Beat = 1;
 151      _MIDI_Measure = 1;
 152      _MIDI_Time = 0;
 153      _MIDI_BeatsPerMeasure = 4;
 154      _MIDI_TicksPerBeat = _MIDI_Division;
 155      _MIDI_TimeBase = 4;
 156      _MIDI_PositionInTicks = 0;
 157      _MIDI_ActiveTracks    = 0;
 158      _MIDI_Context         = 0;
 159  
 160      track *ptr = _MIDI_TrackPtr;
 161      for (bssize_t i = 0; i < _MIDI_NumTracks; ++i)
 162      {
 163          ptr->pos                    = ptr->start;
 164          ptr->delay                  = _MIDI_ReadDelta(ptr);
 165          ptr->active                 = ptr->EMIDI_IncludeTrack;
 166          ptr->RunningStatus          = 0;
 167          ptr->currentcontext         = 0;
 168          ptr->context[0].loopstart = ptr->start;
 169          ptr->context[0].loopcount = 0;
 170  
 171          if (ptr->active)
 172              _MIDI_ActiveTracks++;
 173  
 174          ptr++;
 175      }
 176  }
 177  
 178  static void _MIDI_AdvanceTick(void)
 179  {
 180      _MIDI_PositionInTicks++;
 181      _MIDI_Time += _MIDI_FPSecondsPerTick;
 182  
 183      _MIDI_Tick++;
 184      while (_MIDI_Tick > _MIDI_TicksPerBeat)
 185      {
 186          _MIDI_Tick -= _MIDI_TicksPerBeat;
 187          _MIDI_Beat++;
 188      }
 189      while (_MIDI_Beat > _MIDI_BeatsPerMeasure)
 190      {
 191          _MIDI_Beat -= _MIDI_BeatsPerMeasure;
 192          _MIDI_Measure++;
 193      }
 194  }
 195  
 196  static void _MIDI_SysEx(track *Track)
 197  {
 198      int length = _MIDI_ReadDelta(Track);
 199      Track->pos += length;
 200  }
 201  
 202  
 203  static void _MIDI_MetaEvent(track *Track)
 204  {
 205      int   command;
 206      int   length;
 207  
 208      GET_NEXT_EVENT(Track, command);
 209      GET_NEXT_EVENT(Track, length);
 210  
 211      switch (command)
 212      {
 213          case MIDI_END_OF_TRACK:
 214              Track->active = FALSE;
 215  
 216              _MIDI_ActiveTracks--;
 217              break;
 218  
 219          case MIDI_TEMPO_CHANGE:
 220          {
 221              int tempo = tabledivide32_noinline(60000000L, _MIDI_ReadNumber(Track->pos, 3));
 222              MIDI_SetTempo(tempo);
 223              break;
 224          }
 225  
 226          case MIDI_TIME_SIGNATURE:
 227          {
 228              if ((_MIDI_Tick > 0) || (_MIDI_Beat > 1))
 229                  _MIDI_Measure++;
 230  
 231              _MIDI_Tick = 0;
 232              _MIDI_Beat = 1;
 233              _MIDI_TimeBase = 1;
 234              _MIDI_BeatsPerMeasure = (int)*Track->pos;
 235              int denominator = (int) * (Track->pos + 1);
 236  
 237              while (denominator > 0)
 238              {
 239                  _MIDI_TimeBase += _MIDI_TimeBase;
 240                  denominator--;
 241              }
 242  
 243              _MIDI_TicksPerBeat = tabledivide32_noinline(_MIDI_Division * 4, _MIDI_TimeBase);
 244              break;
 245          }
 246      }
 247  
 248      Track->pos += length;
 249  }
 250  
 251  static int _MIDI_InterpretControllerInfo(track *Track, int TimeSet, int channel, int c1, int c2)
 252  {
 253      track *trackptr;
 254      int tracknum;
 255      int loopcount;
 256  
 257      switch (c1)
 258      {
 259          case MIDI_MONO_MODE_ON :
 260              Track->pos++;
 261              break;
 262  
 263          case MIDI_VOLUME :
 264              if (!Track->EMIDI_VolumeChange)
 265                  _MIDI_SetChannelVolume(channel, c2);
 266              break;
 267  
 268          case EMIDI_INCLUDE_TRACK :
 269          case EMIDI_EXCLUDE_TRACK :
 270              break;
 271  
 272          case EMIDI_PROGRAM_CHANGE :
 273              if (Track->EMIDI_ProgramChange)
 274                  _MIDI_Funcs->ProgramChange(channel, c2 & 0x7f);
 275              break;
 276  
 277          case EMIDI_VOLUME_CHANGE :
 278              if (Track->EMIDI_VolumeChange)
 279                  _MIDI_SetChannelVolume(channel, c2);
 280              break;
 281  
 282          case EMIDI_CONTEXT_START :
 283              break;
 284  
 285          case EMIDI_CONTEXT_END :
 286              if ((Track->currentcontext == _MIDI_Context) || (_MIDI_Context < 0) ||
 287                  (Track->context[_MIDI_Context].pos == nullptr))
 288                  break;
 289  
 290              Track->currentcontext = _MIDI_Context;
 291              Track->context[0].loopstart = Track->context[_MIDI_Context].loopstart;
 292              Track->context[0].loopcount = Track->context[_MIDI_Context].loopcount;
 293              Track->pos           = Track->context[_MIDI_Context].pos;
 294              Track->RunningStatus = Track->context[_MIDI_Context].RunningStatus;
 295  
 296              if (TimeSet)
 297              {
 298                  break;
 299              }
 300  
 301              _MIDI_Time             = Track->context[_MIDI_Context].time;
 302              _MIDI_FPSecondsPerTick = Track->context[_MIDI_Context].FPSecondsPerTick;
 303              _MIDI_Tick             = Track->context[_MIDI_Context].tick;
 304              _MIDI_Beat             = Track->context[_MIDI_Context].beat;
 305              _MIDI_Measure          = Track->context[_MIDI_Context].measure;
 306              _MIDI_BeatsPerMeasure  = Track->context[_MIDI_Context].BeatsPerMeasure;
 307              _MIDI_TicksPerBeat     = Track->context[_MIDI_Context].TicksPerBeat;
 308              _MIDI_TimeBase         = Track->context[_MIDI_Context].TimeBase;
 309              TimeSet = TRUE;
 310              break;
 311  
 312          case EMIDI_LOOP_START :
 313          case EMIDI_SONG_LOOP_START :
 314              loopcount = (c2 == 0) ? EMIDI_INFINITE : c2;
 315  
 316              if (c1 == EMIDI_SONG_LOOP_START)
 317              {
 318                  trackptr = _MIDI_TrackPtr;
 319                  tracknum = _MIDI_NumTracks;
 320              }
 321              else
 322              {
 323                  trackptr = Track;
 324                  tracknum = 1;
 325              }
 326  
 327              while (tracknum > 0)
 328              {
 329                  trackptr->context[0].loopcount        = loopcount;
 330                  trackptr->context[0].pos              = trackptr->pos;
 331                  trackptr->context[0].loopstart        = trackptr->pos;
 332                  trackptr->context[0].RunningStatus    = trackptr->RunningStatus;
 333                  trackptr->context[0].active           = trackptr->active;
 334                  trackptr->context[0].delay            = trackptr->delay;
 335                  trackptr->context[0].time             = _MIDI_Time;
 336                  trackptr->context[0].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
 337                  trackptr->context[0].tick             = _MIDI_Tick;
 338                  trackptr->context[0].beat             = _MIDI_Beat;
 339                  trackptr->context[0].measure          = _MIDI_Measure;
 340                  trackptr->context[0].BeatsPerMeasure  = _MIDI_BeatsPerMeasure;
 341                  trackptr->context[0].TicksPerBeat     = _MIDI_TicksPerBeat;
 342                  trackptr->context[0].TimeBase         = _MIDI_TimeBase;
 343                  trackptr++;
 344                  tracknum--;
 345              }
 346              break;
 347  
 348          case EMIDI_LOOP_END :
 349          case EMIDI_SONG_LOOP_END :
 350              if ((c2 != EMIDI_END_LOOP_VALUE) || (Track->context[0].loopstart == nullptr) || (Track->context[0].loopcount == 0))
 351                  break;
 352  
 353              if (c1 == EMIDI_SONG_LOOP_END)
 354              {
 355                  trackptr = _MIDI_TrackPtr;
 356                  tracknum = _MIDI_NumTracks;
 357                  _MIDI_ActiveTracks = 0;
 358              }
 359              else
 360              {
 361                  trackptr = Track;
 362                  tracknum = 1;
 363                  _MIDI_ActiveTracks--;
 364              }
 365  
 366              while (tracknum > 0)
 367              {
 368                  if (trackptr->context[0].loopcount != EMIDI_INFINITE)
 369                  {
 370                      trackptr->context[0].loopcount--;
 371                  }
 372  
 373                  trackptr->pos           = trackptr->context[0].loopstart;
 374                  trackptr->RunningStatus = trackptr->context[0].RunningStatus;
 375                  trackptr->delay         = trackptr->context[0].delay;
 376                  trackptr->active        = trackptr->context[0].active;
 377                  if (trackptr->active)
 378                  {
 379                      _MIDI_ActiveTracks++;
 380                  }
 381  
 382                  if (!TimeSet)
 383                  {
 384                      _MIDI_Time             = trackptr->context[0].time;
 385                      _MIDI_FPSecondsPerTick = trackptr->context[0].FPSecondsPerTick;
 386                      _MIDI_Tick             = trackptr->context[0].tick;
 387                      _MIDI_Beat             = trackptr->context[0].beat;
 388                      _MIDI_Measure          = trackptr->context[0].measure;
 389                      _MIDI_BeatsPerMeasure  = trackptr->context[0].BeatsPerMeasure;
 390                      _MIDI_TicksPerBeat     = trackptr->context[0].TicksPerBeat;
 391                      _MIDI_TimeBase         = trackptr->context[0].TimeBase;
 392                      TimeSet = TRUE;
 393                  }
 394  
 395                  trackptr++;
 396                  tracknum--;
 397              }
 398              break;
 399  
 400          default :
 401              if (_MIDI_Funcs->ControlChange)
 402                  _MIDI_Funcs->ControlChange(channel, c1, c2);
 403      }
 404  
 405      return TimeSet;
 406  }
 407  
 408  void MIDI_ServiceRoutine(void)
 409  {
 410      if (!_MIDI_SongActive)
 411          return;
 412  
 413      track *Track = _MIDI_TrackPtr;
 414      int tracknum = 0;
 415      int TimeSet = FALSE;
 416      int c1 = 0;
 417      int c2 = 0;
 418  
 419      while (tracknum < _MIDI_NumTracks)
 420      {
 421          while ((Track->active) && (Track->delay == 0))
 422          {
 423              int event;
 424              GET_NEXT_EVENT(Track, event);
 425  
 426              if (GET_MIDI_COMMAND(event) == MIDI_SPECIAL)
 427              {
 428                  switch (event)
 429                  {
 430                      case MIDI_SYSEX:
 431                      case MIDI_SYSEX_CONTINUE: _MIDI_SysEx(Track); break;
 432                      case MIDI_META_EVENT: _MIDI_MetaEvent(Track); break;
 433                  }
 434  
 435                  if (Track->active)
 436                      Track->delay = _MIDI_ReadDelta(Track);
 437                  continue;
 438              }
 439  
 440              if (event & MIDI_RUNNING_STATUS)
 441                  Track->RunningStatus = event;
 442              else
 443              {
 444                  event = Track->RunningStatus;
 445                  Track->pos--;
 446              }
 447  
 448              int const channel = GET_MIDI_CHANNEL(event);
 449              int const command = GET_MIDI_COMMAND(event);
 450  
 451              if (_MIDI_CommandLengths[command] > 0)
 452              {
 453                  GET_NEXT_EVENT(Track, c1);
 454                  if (_MIDI_CommandLengths[command] > 1)
 455                      GET_NEXT_EVENT(Track, c2);
 456              }
 457  
 458              switch (command)
 459              {
 460                  case MIDI_NOTE_OFF:
 461                      if (_MIDI_Funcs->NoteOff)
 462                          _MIDI_Funcs->NoteOff(channel, c1, c2);
 463                      break;
 464  
 465                  case MIDI_NOTE_ON:
 466                      if (_MIDI_Funcs->NoteOn)
 467                          _MIDI_Funcs->NoteOn(channel, c1, c2);
 468                      break;
 469  
 470                  case MIDI_POLY_AFTER_TCH:
 471                      if (_MIDI_Funcs->PolyAftertouch)
 472                          _MIDI_Funcs->PolyAftertouch(channel, c1, c2);
 473                      break;
 474  
 475                  case MIDI_CONTROL_CHANGE:
 476                      TimeSet = _MIDI_InterpretControllerInfo(Track, TimeSet, channel, c1, c2);
 477                      break;
 478  
 479                  case MIDI_PROGRAM_CHANGE:
 480                      if ((_MIDI_Funcs->ProgramChange) && (!Track->EMIDI_ProgramChange))
 481                          _MIDI_Funcs->ProgramChange(channel, c1 & 0x7f);
 482                      break;
 483  
 484                  case MIDI_AFTER_TOUCH:
 485                      if (_MIDI_Funcs->ChannelAftertouch)
 486                          _MIDI_Funcs->ChannelAftertouch(channel, c1);
 487                      break;
 488  
 489                  case MIDI_PITCH_BEND:
 490                      if (_MIDI_Funcs->PitchBend)
 491                          _MIDI_Funcs->PitchBend(channel, c1, c2);
 492                      break;
 493  
 494                  default: break;
 495              }
 496  
 497              Track->delay = _MIDI_ReadDelta(Track);
 498          }
 499  
 500          Track->delay--;
 501          Track++;
 502          tracknum++;
 503  
 504          if (_MIDI_ActiveTracks == 0)
 505          {
 506              _MIDI_ResetTracks();
 507              if (_MIDI_Loop)
 508              {
 509                  tracknum = 0;
 510                  Track = _MIDI_TrackPtr;
 511              }
 512              else
 513              {
 514                  _MIDI_SongActive = FALSE;
 515                  break;
 516              }
 517          }
 518      }
 519  
 520      _MIDI_AdvanceTick();
 521      _MIDI_GlobalPositionInTicks++;
 522  }
 523  
 524  static int _MIDI_SendControlChange(int channel, int c1, int c2)
 525  {
 526      if (_MIDI_Funcs == nullptr || _MIDI_Funcs->ControlChange == nullptr)
 527          return MIDI_Error;
 528  
 529      _MIDI_Funcs->ControlChange(channel, c1, c2);
 530  
 531      return MIDI_Ok;
 532  }
 533  
 534  static int _MIDI_SendProgramChange(int channel, int c1)
 535  {
 536      if (_MIDI_Funcs == nullptr || _MIDI_Funcs->ProgramChange == nullptr)
 537          return MIDI_Error;
 538  
 539      _MIDI_Funcs->ProgramChange(channel, c1);
 540  
 541      return MIDI_Ok;
 542  }
 543  
 544  int MIDI_AllNotesOff(void)
 545  {
 546      SoundDriver_MIDI_Lock();
 547  
 548      for (bssize_t channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
 549      {
 550          _MIDI_SendControlChange(channel, MIDI_HOLD1, 0);
 551          _MIDI_SendControlChange(channel, MIDI_SOSTENUTO, 0);
 552          _MIDI_SendControlChange(channel, MIDI_ALL_NOTES_OFF, 0);
 553          _MIDI_SendControlChange(channel, MIDI_ALL_SOUNDS_OFF, 0);
 554      }
 555  
 556      SoundDriver_MIDI_Unlock();
 557  
 558      return MIDI_Ok;
 559  }
 560  
 561  static void _MIDI_SetChannelVolume(int channel, int volume)
 562  {
 563      _MIDI_ChannelVolume[channel] = volume;
 564  
 565      if (_MIDI_Funcs == nullptr || _MIDI_Funcs->ControlChange == nullptr)
 566          return;
 567  
 568      if (_MIDI_Funcs->SetVolume == nullptr)
 569      {
 570          volume *= _MIDI_TotalVolume;
 571          volume = tabledivide32_noinline(volume, MIDI_MaxVolume);
 572      }
 573  
 574      _MIDI_Funcs->ControlChange(channel, MIDI_VOLUME, volume);
 575  }
 576  
 577  static void _MIDI_SendChannelVolumes(void)
 578  {
 579      for (bssize_t channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
 580          _MIDI_SetChannelVolume(channel, _MIDI_ChannelVolume[channel]);
 581  }
 582  
 583  int MIDI_Reset(void)
 584  {
 585      MIDI_AllNotesOff();
 586  
 587      SoundDriver_MIDI_Lock();
 588  
 589      for (bssize_t channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
 590      {
 591          _MIDI_SendControlChange(channel, MIDI_RESET_ALL_CONTROLLERS, 0);
 592          _MIDI_SendControlChange(channel, MIDI_RPN_MSB, MIDI_PITCHBEND_MSB);
 593          _MIDI_SendControlChange(channel, MIDI_RPN_LSB, MIDI_PITCHBEND_LSB);
 594          _MIDI_SendControlChange(channel, MIDI_DATAENTRY_MSB, 2); /* Pitch Bend Sensitivity MSB */
 595          _MIDI_SendControlChange(channel, MIDI_DATAENTRY_LSB, 0); /* Pitch Bend Sensitivity LSB */
 596          _MIDI_ChannelVolume[channel] = GENMIDI_DefaultVolume;
 597          _MIDI_SendControlChange(channel, MIDI_PAN, 64);  // begin TURRICAN's recommendation
 598          _MIDI_SendControlChange(channel, MIDI_REVERB, 40);
 599          _MIDI_SendControlChange(channel, MIDI_CHORUS, 0);
 600          _MIDI_SendControlChange(channel, MIDI_BANK_SELECT_MSB, 0);
 601          _MIDI_SendControlChange(channel, MIDI_BANK_SELECT_LSB, 0);
 602          _MIDI_SendProgramChange(channel, 0);  // end TURRICAN's recommendation
 603      }
 604  
 605      _MIDI_SendChannelVolumes();
 606  
 607      SoundDriver_MIDI_Unlock();
 608  
 609      _MIDI_Reset = TRUE;
 610  
 611      return MIDI_Ok;
 612  }
 613  
 614  
 615  int MIDI_SetVolume(int volume)
 616  {
 617      if (_MIDI_Funcs == nullptr)
 618          return MIDI_NullMidiModule;
 619  
 620      volume = min(MIDI_MaxVolume, volume);
 621      volume = max(0, volume);
 622  
 623      _MIDI_TotalVolume = volume;
 624  
 625      SoundDriver_MIDI_Lock();
 626  
 627      if (_MIDI_Funcs->SetVolume)
 628          _MIDI_Funcs->SetVolume(volume);
 629      else
 630          _MIDI_SendChannelVolumes();
 631  
 632      SoundDriver_MIDI_Unlock();
 633  
 634      return MIDI_Ok;
 635  }
 636  
 637  
 638  int MIDI_GetVolume(void)
 639  {
 640      if (_MIDI_Funcs == nullptr)
 641          return MIDI_NullMidiModule;
 642  
 643      SoundDriver_MIDI_Lock();
 644      int volume = (_MIDI_Funcs->GetVolume) ? _MIDI_Funcs->GetVolume() : _MIDI_TotalVolume;
 645      SoundDriver_MIDI_Unlock();
 646  
 647      return volume;
 648  }
 649  
 650  void MIDI_SetLoopFlag(int loopflag) { _MIDI_Loop = loopflag; }
 651  
 652  void MIDI_ContinueSong(void)
 653  {
 654      if (!_MIDI_SongLoaded)
 655          return;
 656  
 657      _MIDI_SongActive = TRUE;
 658  }
 659  
 660  void MIDI_PauseSong(void)
 661  {
 662      if (!_MIDI_SongLoaded)
 663          return;
 664  
 665      _MIDI_SongActive = FALSE;
 666      MIDI_AllNotesOff();
 667  }
 668  
 669  void MIDI_SetMidiFuncs(midifuncs *funcs) { _MIDI_Funcs = funcs; }
 670  
 671  void MIDI_StopSong(void)
 672  {
 673      if (!_MIDI_SongLoaded)
 674          return;
 675  
 676      SoundDriver_MIDI_HaltPlayback();
 677  
 678      _MIDI_SongActive = FALSE;
 679      _MIDI_SongLoaded = FALSE;
 680  
 681      MIDI_Reset();
 682      _MIDI_ResetTracks();
 683  
 684      DO_FREE_AND_NULL(_MIDI_TrackPtr);
 685  
 686      _MIDI_NumTracks    = 0;
 687      _MIDI_TrackMemSize = 0;
 688  
 689      _MIDI_TotalTime     = 0;
 690      _MIDI_TotalTicks    = 0;
 691      _MIDI_TotalBeats    = 0;
 692      _MIDI_TotalMeasures = 0;
 693  }
 694  
 695  static void _MIDI_InitEMIDI(void);
 696  
 697  int MIDI_PlaySong(char *song, int loopflag)
 698  {
 699      if (_MIDI_Funcs == nullptr)
 700          return MIDI_NullMidiModule;
 701  
 702      if (B_UNBUF32(song) != MIDI_HEADER_SIGNATURE)
 703          return MIDI_InvalidMidiFile;
 704  
 705      _MIDI_SongPtr = song;
 706  
 707      song += 4;
 708      int const headersize = _MIDI_ReadNumber(song, 4);
 709      song += 4;
 710      int const format = _MIDI_ReadNumber(song, 2);
 711  
 712      int My_MIDI_NumTracks = _MIDI_ReadNumber(song + 2, 2);
 713      int My_MIDI_Division  = _MIDI_ReadNumber(song + 4, 2);
 714  
 715      if (My_MIDI_Division < 0)
 716      {
 717          // If a SMPTE time division is given, just set to 96 so no errors occur
 718          My_MIDI_Division = 96;
 719      }
 720  
 721      if (format > MAX_FORMAT)
 722          return MIDI_UnknownMidiFormat;
 723  
 724      char *ptr = song + headersize;
 725  
 726      if (My_MIDI_NumTracks == 0)
 727          return MIDI_NoTracks;
 728  
 729      int My_MIDI_TrackMemSize = My_MIDI_NumTracks  * sizeof(track);
 730      track * My_MIDI_TrackPtr = (track *)Xmalloc(My_MIDI_TrackMemSize);
 731  
 732      auto CurrentTrack = My_MIDI_TrackPtr;
 733      int numtracks    = My_MIDI_NumTracks;
 734  
 735      while (numtracks--)
 736      {
 737          if (B_UNBUF32(ptr) != MIDI_TRACK_SIGNATURE)
 738          {
 739              DO_FREE_AND_NULL(My_MIDI_TrackPtr);
 740  
 741              My_MIDI_TrackMemSize = 0;
 742  
 743              return MIDI_InvalidTrack;
 744          }
 745  
 746          int tracklength = _MIDI_ReadNumber(ptr + 4, 4);
 747          ptr += 8;
 748          CurrentTrack->start = ptr;
 749          CurrentTrack->EMIDI_IncludeTrack = FALSE;
 750          ptr += tracklength;
 751          CurrentTrack++;
 752      }
 753  
 754      // at this point we know song load is successful
 755  
 756      if (_MIDI_SongLoaded)
 757          MIDI_StopSong();
 758  
 759      _MIDI_Loop = loopflag;
 760      _MIDI_NumTracks = My_MIDI_NumTracks;
 761      _MIDI_Division = My_MIDI_Division;
 762      _MIDI_TrackMemSize = My_MIDI_TrackMemSize;
 763      _MIDI_TrackPtr = My_MIDI_TrackPtr;
 764  
 765      _MIDI_InitEMIDI();
 766      _MIDI_ResetTracks();
 767  
 768      if (!_MIDI_Reset)
 769          MIDI_Reset();
 770  
 771      _MIDI_Reset = FALSE;
 772  
 773      // this can either stay like this, or I can add another field to the MIDI driver spec that holds the service callback
 774      if (SoundDriver_MIDI_StartPlayback() != MIDI_Ok)
 775          return MIDI_DriverError;
 776  
 777      MIDI_SetTempo(120);
 778  
 779      _MIDI_SongLoaded = TRUE;
 780      _MIDI_SongActive = TRUE;
 781  
 782      return MIDI_Ok;
 783  }
 784  
 785  void MIDI_SetTempo(int tempo)
 786  {
 787      SoundDriver_MIDI_SetTempo(tempo, _MIDI_Division);
 788      int const tickspersecond = tempo * _MIDI_Division / 60;
 789      _MIDI_FPSecondsPerTick = tabledivide32_noinline(1 << TIME_PRECISION, tickspersecond);
 790  }
 791  
 792  static int _MIDI_ProcessNextTick(void)
 793  {
 794      if (_MIDI_TrackPtr == nullptr)
 795          return 0;
 796  
 797      int TimeSet  = FALSE;
 798      int tracknum = 0;
 799  
 800      track *Track = _MIDI_TrackPtr;
 801  
 802      while (tracknum < _MIDI_NumTracks)
 803      {
 804          while ((Track->active) && (Track->delay == 0))
 805          {
 806              int event;
 807  
 808              GET_NEXT_EVENT(Track, event);
 809  
 810              if (GET_MIDI_COMMAND(event) == MIDI_SPECIAL)
 811              {
 812                  switch (event)
 813                  {
 814                      case MIDI_SYSEX:
 815                      case MIDI_SYSEX_CONTINUE:
 816                          _MIDI_SysEx(Track);
 817                          break;
 818  
 819                      case MIDI_META_EVENT:
 820                          _MIDI_MetaEvent(Track);
 821                          break;
 822                  }
 823  
 824                  if (Track->active)
 825                      Track->delay = _MIDI_ReadDelta(Track);
 826  
 827                  continue;
 828              }
 829  
 830              if (event & MIDI_RUNNING_STATUS)
 831                  Track->RunningStatus = event;
 832              else
 833              {
 834                  event = Track->RunningStatus;
 835                  Track->pos--;
 836              }
 837  
 838              int channel = GET_MIDI_CHANNEL(event);
 839              int command = GET_MIDI_COMMAND(event);
 840  
 841              int c1 = 0;
 842              int c2 = 0;
 843  
 844              if (_MIDI_CommandLengths[command] > 0)
 845              {
 846                  GET_NEXT_EVENT(Track, c1);
 847                  if (_MIDI_CommandLengths[command] > 1)
 848                      GET_NEXT_EVENT(Track, c2);
 849              }
 850  
 851              switch (command)
 852              {
 853                  case MIDI_NOTE_OFF:
 854                      break;
 855  
 856                  case MIDI_NOTE_ON:
 857                      break;
 858  
 859                  case MIDI_POLY_AFTER_TCH:
 860                      if (_MIDI_Funcs->PolyAftertouch)
 861                          _MIDI_Funcs->PolyAftertouch(channel, c1, c2);
 862                      break;
 863  
 864                  case MIDI_CONTROL_CHANGE:
 865                      TimeSet = _MIDI_InterpretControllerInfo(Track, TimeSet, channel, c1, c2);
 866                      break;
 867  
 868                  case MIDI_PROGRAM_CHANGE:
 869                      if ((_MIDI_Funcs->ProgramChange) && (!Track->EMIDI_ProgramChange))
 870                          _MIDI_Funcs->ProgramChange(channel, c1);
 871                      break;
 872  
 873                  case MIDI_AFTER_TOUCH:
 874                      if (_MIDI_Funcs->ChannelAftertouch)
 875                          _MIDI_Funcs->ChannelAftertouch(channel, c1);
 876                      break;
 877  
 878                  case MIDI_PITCH_BEND:
 879                      if (_MIDI_Funcs->PitchBend)
 880                          _MIDI_Funcs->PitchBend(channel, c1, c2);
 881                      break;
 882  
 883                  default:
 884                      break;
 885              }
 886  
 887              Track->delay = _MIDI_ReadDelta(Track);
 888          }
 889  
 890          Track->delay--;
 891          Track++;
 892          tracknum++;
 893  
 894          if (_MIDI_ActiveTracks == 0)
 895              break;
 896      }
 897  
 898      _MIDI_AdvanceTick();
 899  
 900      return TimeSet;
 901  }
 902  
 903  void MIDI_SetSongPosition(int measure, int beat, int tick)
 904  {
 905      if (!_MIDI_SongLoaded)
 906          return;
 907  
 908      MIDI_PauseSong();
 909  
 910      int32_t pos = RELATIVE_BEAT(measure, beat, tick);
 911  
 912      if (pos < RELATIVE_BEAT(_MIDI_Measure, _MIDI_Beat, _MIDI_Tick))
 913      {
 914          SoundDriver_MIDI_Lock();
 915          _MIDI_ResetTracks();
 916          SoundDriver_MIDI_Unlock();
 917  
 918          MIDI_Reset();
 919      }
 920  
 921      SoundDriver_MIDI_Lock();
 922      while (RELATIVE_BEAT(_MIDI_Measure, _MIDI_Beat, _MIDI_Tick) < pos)
 923      {
 924          if (_MIDI_ProcessNextTick())
 925              break;
 926  
 927          if (_MIDI_ActiveTracks == 0)
 928          {
 929              _MIDI_ResetTracks();
 930              if (!_MIDI_Loop)
 931              {
 932                  SoundDriver_MIDI_Unlock();
 933                  return;
 934              }
 935              break;
 936          }
 937      }
 938      SoundDriver_MIDI_Unlock();
 939  
 940      MIDI_SetVolume(_MIDI_TotalVolume);
 941      MIDI_ContinueSong();
 942  }
 943  
 944  void MIDI_GetSongPosition(songposition *pos)
 945  {
 946      uint32_t mil      = (_MIDI_Time & ((1 << TIME_PRECISION) - 1)) * 1000;
 947      uint32_t sec      = _MIDI_Time >> TIME_PRECISION;
 948      pos->milliseconds = (mil >> TIME_PRECISION) + (sec * 1000);
 949      pos->tickposition = _MIDI_PositionInTicks;
 950      pos->measure      = _MIDI_Measure;
 951      pos->beat         = _MIDI_Beat;
 952      pos->tick         = _MIDI_Tick;
 953  }
 954  
 955  static void _MIDI_InitEMIDI(void)
 956  {
 957      int const type = (ASS_EMIDICard != -1) ? ASS_EMIDICard : SoundDriver_MIDI_GetCardType();
 958  
 959      _MIDI_ResetTracks();
 960  
 961      _MIDI_TotalTime     = 0;
 962      _MIDI_TotalTicks    = 0;
 963      _MIDI_TotalBeats    = 0;
 964      _MIDI_TotalMeasures = 0;
 965  
 966      track *Track = _MIDI_TrackPtr;
 967      int tracknum = 0;
 968  
 969      while ((tracknum < _MIDI_NumTracks) && (Track != nullptr))
 970      {
 971          _MIDI_Tick = 0;
 972          _MIDI_Beat = 1;
 973          _MIDI_Measure = 1;
 974          _MIDI_Time = 0;
 975          _MIDI_BeatsPerMeasure = 4;
 976          _MIDI_TicksPerBeat = _MIDI_Division;
 977          _MIDI_TimeBase = 4;
 978  
 979          _MIDI_PositionInTicks = 0;
 980          _MIDI_ActiveTracks    = 0;
 981          _MIDI_Context         = -1;
 982  
 983          Track->RunningStatus = 0;
 984          Track->active        = TRUE;
 985  
 986          Track->EMIDI_ProgramChange = FALSE;
 987          Track->EMIDI_VolumeChange  = FALSE;
 988          Track->EMIDI_IncludeTrack  = TRUE;
 989  
 990          memset(Track->context, 0, sizeof(Track->context));
 991  
 992          while (Track->delay > 0)
 993          {
 994              _MIDI_AdvanceTick();
 995              Track->delay--;
 996          }
 997  
 998          int IncludeFound = FALSE;
 999  
1000          while (Track->active)
1001          {
1002              int event;
1003  
1004              GET_NEXT_EVENT(Track, event);
1005  
1006              if (GET_MIDI_COMMAND(event) == MIDI_SPECIAL)
1007              {
1008                  switch (event)
1009                  {
1010                      case MIDI_SYSEX:
1011                      case MIDI_SYSEX_CONTINUE: _MIDI_SysEx(Track); break;
1012                      case MIDI_META_EVENT: _MIDI_MetaEvent(Track); break;
1013                  }
1014  
1015                  if (Track->active)
1016                  {
1017                      Track->delay = _MIDI_ReadDelta(Track);
1018                      while (Track->delay > 0)
1019                      {
1020                          _MIDI_AdvanceTick();
1021                          Track->delay--;
1022                      }
1023                  }
1024  
1025                  continue;
1026              }
1027  
1028              if (event & MIDI_RUNNING_STATUS)
1029                  Track->RunningStatus = event;
1030              else
1031              {
1032                  event = Track->RunningStatus;
1033                  Track->pos--;
1034              }
1035  
1036  //            channel = GET_MIDI_CHANNEL(event);
1037              int const command = GET_MIDI_COMMAND(event);
1038              int length = _MIDI_CommandLengths[command];
1039  
1040              if (command == MIDI_CONTROL_CHANGE)
1041              {
1042                  if (*Track->pos == MIDI_MONO_MODE_ON)
1043                      length++;
1044  
1045                  int c1, c2;
1046                  GET_NEXT_EVENT(Track, c1);
1047                  GET_NEXT_EVENT(Track, c2);
1048                  length -= 2;
1049  
1050                  switch (c1)
1051                  {
1052                  case EMIDI_LOOP_START :
1053                  case EMIDI_SONG_LOOP_START :
1054                      Track->context[0].loopcount        = (c2 == 0) ? EMIDI_INFINITE : c2;
1055                      Track->context[0].pos              = Track->pos;
1056                      Track->context[0].loopstart        = Track->pos;
1057                      Track->context[0].RunningStatus    = Track->RunningStatus;
1058                      Track->context[0].time             = _MIDI_Time;
1059                      Track->context[0].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
1060                      Track->context[0].tick             = _MIDI_Tick;
1061                      Track->context[0].beat             = _MIDI_Beat;
1062                      Track->context[0].measure          = _MIDI_Measure;
1063                      Track->context[0].BeatsPerMeasure  = _MIDI_BeatsPerMeasure;
1064                      Track->context[0].TicksPerBeat     = _MIDI_TicksPerBeat;
1065                      Track->context[0].TimeBase         = _MIDI_TimeBase;
1066                      break;
1067  
1068                  case EMIDI_LOOP_END :
1069                  case EMIDI_SONG_LOOP_END :
1070                      if (c2 == EMIDI_END_LOOP_VALUE)
1071                      {
1072                          Track->context[0].loopstart = nullptr;
1073                          Track->context[0].loopcount = 0;
1074                      }
1075                      break;
1076  
1077                  case EMIDI_INCLUDE_TRACK :
1078                      if (EMIDI_AffectsCurrentCard(c2, type))
1079                      {
1080                          //printf( "Include track %d on card %d\n", tracknum, c2 );
1081                          IncludeFound = TRUE;
1082                          Track->EMIDI_IncludeTrack = TRUE;
1083                      }
1084                      else if (!IncludeFound)
1085                      {
1086                          //printf( "Track excluded %d on card %d\n", tracknum, c2 );
1087                          IncludeFound = TRUE;
1088                          Track->EMIDI_IncludeTrack = FALSE;
1089                      }
1090                      break;
1091  
1092                  case EMIDI_EXCLUDE_TRACK :
1093                      if (EMIDI_AffectsCurrentCard(c2, type))
1094                      {
1095                          //printf( "Exclude track %d on card %d\n", tracknum, c2 );
1096                          Track->EMIDI_IncludeTrack = FALSE;
1097                      }
1098                      break;
1099  
1100                  case EMIDI_PROGRAM_CHANGE :
1101                      if (!Track->EMIDI_ProgramChange)
1102                          //printf( "Program change on track %d\n", tracknum );
1103                          Track->EMIDI_ProgramChange = TRUE;
1104                      break;
1105  
1106                  case EMIDI_VOLUME_CHANGE :
1107                      if (!Track->EMIDI_VolumeChange)
1108                          //printf( "Volume change on track %d\n", tracknum );
1109                          Track->EMIDI_VolumeChange = TRUE;
1110                      break;
1111  
1112                  case EMIDI_CONTEXT_START :
1113                      if ((c2 > 0) && (c2 < EMIDI_NUM_CONTEXTS))
1114                      {
1115                          Track->context[c2].pos              = Track->pos;
1116                          Track->context[c2].loopstart        = Track->context[0].loopstart;
1117                          Track->context[c2].loopcount        = Track->context[0].loopcount;
1118                          Track->context[c2].RunningStatus    = Track->RunningStatus;
1119                          Track->context[c2].time             = _MIDI_Time;
1120                          Track->context[c2].FPSecondsPerTick = _MIDI_FPSecondsPerTick;
1121                          Track->context[c2].tick             = _MIDI_Tick;
1122                          Track->context[c2].beat             = _MIDI_Beat;
1123                          Track->context[c2].measure          = _MIDI_Measure;
1124                          Track->context[c2].BeatsPerMeasure  = _MIDI_BeatsPerMeasure;
1125                          Track->context[c2].TicksPerBeat     = _MIDI_TicksPerBeat;
1126                          Track->context[c2].TimeBase         = _MIDI_TimeBase;
1127                      }
1128                      break;
1129  
1130                  case EMIDI_CONTEXT_END :
1131                      break;
1132                  }
1133              }
1134  
1135              Track->pos += length;
1136              Track->delay = _MIDI_ReadDelta(Track);
1137  
1138              while (Track->delay > 0)
1139              {
1140                  _MIDI_AdvanceTick();
1141                  Track->delay--;
1142              }
1143          }
1144  
1145          _MIDI_TotalTime = max(_MIDI_TotalTime, _MIDI_Time);
1146          if (RELATIVE_BEAT(_MIDI_Measure, _MIDI_Beat, _MIDI_Tick) >
1147              RELATIVE_BEAT(_MIDI_TotalMeasures, _MIDI_TotalBeats, _MIDI_TotalTicks))
1148          {
1149              _MIDI_TotalTicks = _MIDI_Tick;
1150              _MIDI_TotalBeats = _MIDI_Beat;
1151              _MIDI_TotalMeasures = _MIDI_Measure;
1152          }
1153  
1154          Track++;
1155          tracknum++;
1156      }
1157  
1158      _MIDI_ResetTracks();
1159  }