//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: midiseq.cpp,v 1.30.2.21 2009/12/20 05:00:35 terminator356 Exp $
//
//    high priority task for scheduling midi events
//
//  (C) Copyright 2003 Werner Schweer (ws@seh.de)
//
//  This program 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 of
//  the License, or (at your option) any later version.
//
//  This program 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.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
//=========================================================

#include <QMessageBox>

#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <math.h>

#include "globals.h"
#include "midi.h"
#include "midiseq.h"
#include "midiport.h"
#include "mididev.h"
#include "midictrl.h"
#include "audio.h"
#include "driver/alsamidi.h"
#include "driver/jackmidi.h"
#include "sync.h"
#include "synth.h"
#include "song.h"
#include "gconfig.h"

MidiSeq* midiSeq;
int MidiSeq::ticker = 0;
volatile bool midiBusy=false;


//---------------------------------------------------------
//   readMsg
//---------------------------------------------------------

static void readMsg(void* p, void*)
      {
      
      MidiSeq* at = (MidiSeq*)p;
      at->readMsg();
      }

//---------------------------------------------------------
//   processMsg
//---------------------------------------------------------

void MidiSeq::processMsg(const ThreadMsg* m)
      {
      AudioMsg* msg = (AudioMsg*)m;
      switch(msg->id) {
            // This does not appear to be used anymore. Was sent in Audio::process1, 
            //  now Audio::processMidi is called directly. p4.0.15 Tim.
            //case MS_PROCESS:
            //      audio->processMidi();
            //      break;
            
            // Removed p4.0.34
            //case SEQM_SEEK:
            //      processSeek();
            //      break;
            //case MS_STOP:
            //      processStop();
            //      break;
            
            case MS_SET_RTC:
                  MusEGlobal::doSetuid();
                  setRtcTicks();
                  MusEGlobal::undoSetuid();
                  break;
            case MS_UPDATE_POLL_FD:
                  updatePollFd();
                  break;
            // Moved into Song::processMsg p4.0.34
            /*
            case SEQM_ADD_TRACK:
                  song->insertTrack2(msg->track, msg->ival);
                  updatePollFd();
                  break;
            case SEQM_REMOVE_TRACK:
                  song->cmdRemoveTrack(msg->track);
                  updatePollFd();
                  break;
            case SEQM_CHANGE_TRACK:
                  song->changeTrack((Track*)(msg->p1), (Track*)(msg->p2));
                  updatePollFd();
                  break;
            case SEQM_ADD_PART:
                  song->cmdAddPart((Part*)msg->p1);
                  break;
            case SEQM_REMOVE_PART:
                  song->cmdRemovePart((Part*)msg->p1);
                  break;
            case SEQM_CHANGE_PART:
                  //song->cmdChangePart((Part*)msg->p1, (Part*)msg->p2);
                  song->cmdChangePart((Part*)msg->p1, (Part*)msg->p2, msg->a, msg->b);
                  break;
            */
            case SEQM_SET_TRACK_OUT_CHAN:
                  {
                  MidiTrack* track = (MidiTrack*)(msg->p1);
                  track->setOutChanAndUpdate(msg->a);
                  }
                  break;
            case SEQM_SET_TRACK_OUT_PORT:
                  {
                  MidiTrack* track = (MidiTrack*)(msg->p1);
                  track->setOutPortAndUpdate(msg->a);
                  }
                  break;
            case SEQM_REMAP_PORT_DRUM_CTL_EVS:
                    song->remapPortDrumCtrlEvents(msg->ival, msg->a, msg->b, msg->c);
                  break;
            case SEQM_CHANGE_ALL_PORT_DRUM_CTL_EVS:      
                    song->changeAllPortDrumCtrlEvents((bool)msg->a, (bool)msg->b);
                  break;
            case SEQM_SET_MIDI_DEVICE:
                  ((MidiPort*)(msg->p1))->setMidiDevice((MidiDevice*)(msg->p2));
                  updatePollFd();
                  break;
            case SEQM_IDLE:
                  idle = msg->a;
                  break;
            default:
                  printf("MidiSeq::processMsg() unknown id %d\n", msg->id);
                  break;
            }
      }

#if 0   
// Removed p4.0.34
//---------------------------------------------------------
//   processStop
//---------------------------------------------------------

void MidiSeq::processStop()
{
  // p3.3.28
  // TODO Try to move this into Audio::stopRolling(). p4.0.22   // Done p4.0.34
  //playStateExt = false; // not playing
  
  //
  //    clear Alsa midi device notes and stop stuck notes
  //    Jack midi devices are handled in Audio::stopRolling()
  //
  for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) 
  {
    //MidiDevice* md = *id;
    // Only ALSA devices are handled by this thread.
    if((*id)->deviceType() == MidiDevice::ALSA_MIDI)      // p4.0.22
      (*id)->handleStop();  // p4.0.22
    /*
    if (md->midiPort() == -1)
          continue;
    MPEventList* pel = md->playEvents();
    MPEventList* sel = md->stuckNotes();
    pel->clear();
    for(iMPEvent i = sel->begin(); i != sel->end(); ++i) 
    {
      MidiPlayEvent ev = *i;
      ev.setTime(0);
      pel->add(ev);
    }
    sel->clear();
    //md->setNextPlayEvent(pel->begin());  // Removed p4.0.15
    */
  }
}
#endif

#if 0   
// Removed p4.0.34
//---------------------------------------------------------
//   processSeek
//---------------------------------------------------------

void MidiSeq::processSeek()
{
  //int pos = audio->tickPos();
  // TODO Try to move this into audio::seek().   p4.0.22  Done p4.0.34
  //if (pos == 0 && !song->record())
  //      audio->initDevices();

  //---------------------------------------------------
  //    set all controller
  //---------------------------------------------------

  for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i) 
  {
    //MidiDevice* md = *i;
    // Only ALSA devices are handled by this thread.
    if((*i)->deviceType() == MidiDevice::ALSA_MIDI)      // p4.0.22
      (*i)->handleSeek();  // p4.0.22
    /*
    int port = md->midiPort();
    if (port == -1)
          continue;
    MidiPort* mp = &midiPorts[port];
    MidiCtrlValListList* cll = mp->controller();

    MPEventList* el = md->playEvents();

    if (audio->isPlaying()) 
    {
      // stop all notes
      el->clear();
      MPEventList* sel = dev->stuckNotes();
      for (iMPEvent i = sel->begin(); i != sel->end(); ++i) 
      {
            MidiPlayEvent ev = *i;
            ev.setTime(0);
            el->add(ev);
      }
      sel->clear();
    }
    //else
      // Removed p4.0.15 Device now leaves beginning pointing at next event,
      //  immediately after playing some notes.  
      // NOTE: This removal needs testing. I'm not sure about this.
      //el->erase(el->begin(), dev->nextPlayEvent());  
    
    for (iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl) 
    {
      MidiCtrlValList* vl = ivl->second;
      //int val = vl->value(pos);
      //if (val != CTRL_VAL_UNKNOWN) {
      //      int channel = ivl->first >> 24;
      //      el->add(MidiPlayEvent(0, port, channel, ME_CONTROLLER, vl->num(), val));
      //      }
      iMidiCtrlVal imcv = vl->iValue(pos);
      if(imcv != vl->end()) 
      {
        Part* p = imcv->second.part;
        unsigned t = (unsigned)imcv->first;
        // Do not add values that are outside of the part.
        if(p && t >= p->tick() && t < (p->tick() + p->lenTick()) )
          el->add(MidiPlayEvent(0, port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val));
      }
    }
    //dev->setNextPlayEvent(el->begin());    // Removed p4.0.15
    */
  }
}
#endif

//---------------------------------------------------------
//   MidiSeq
//---------------------------------------------------------

//MidiSeq::MidiSeq(int priority, const char* name)
//   : Thread(priority, name)
MidiSeq::MidiSeq(const char* name)
   : Thread(name)
      {
      // Changed by Tim. p3.3.17
      //prio = priority;
      prio = 0;
      
      idle = false;
      midiClock = 0;
      mclock1 = 0.0;
      mclock2 = 0.0;
      songtick1 = songtick2 = 0;
      lastTempo = 0;
      storedtimediffs = 0;
      playStateExt = false; // not playing
      MusEGlobal::doSetuid();
      timerFd=selectTimer();
      MusEGlobal::undoSetuid();

      }

//---------------------------------------------------------
//   ~MidiSeq
//---------------------------------------------------------

MidiSeq::~MidiSeq()
    {
    delete timer;
    }

//---------------------------------------------------------
//   selectTimer()
//   select one of the supported timers to use during this run
//---------------------------------------------------------

signed int MidiSeq::selectTimer()
    {
    int tmrFd;
    
    printf("Trying RTC timer...\n");
    timer = new RtcTimer();
    tmrFd = timer->initTimer();
    if (tmrFd != -1) { // ok!
        printf("got timer = %d\n", tmrFd);
        return tmrFd;
    }
    delete timer;
    
    printf("Trying ALSA timer...\n");
    timer = new AlsaTimer();
    tmrFd = timer->initTimer();
    if ( tmrFd!= -1) { // ok!
        printf("got timer = %d\n", tmrFd);
        return tmrFd;
    }
    delete timer;
    timer=NULL;
    QMessageBox::critical( 0, /*tr*/(QString("Failed to start timer!")),
              /*tr*/(QString("No functional timer was available.\n"
                         "RTC timer not available, check if /dev/rtc is available and readable by current user\n"
                         "Alsa timer not available, check if module snd_timer is available and /dev/snd/timer is available")));
    printf("No functional timer available!!!\n");
    exit(1);
    }

//---------------------------------------------------------
//   threadStart
//    called from loop()
//---------------------------------------------------------

void MidiSeq::threadStart(void*)
      {
      // Removed by Tim. p3.3.17
      /*
      struct sched_param rt_param;
      memset(&rt_param, 0, sizeof(rt_param));
      int prio_min = sched_get_priority_min(SCHED_FIFO);
      int prio_max = sched_get_priority_max(SCHED_FIFO);

      if (prio < prio_min) prio = prio_min;
      else if (prio > prio_max) prio = prio_max;

      rt_param.sched_priority = prio;
      int rv = pthread_setschedparam(pthread_self(), SCHED_FIFO, &rt_param);
      if (rv != 0)
            perror("set realtime scheduler");
      */

      int policy;
      if ((policy = sched_getscheduler (0)) < 0) {
            printf("Cannot get current client scheduler: %s\n", strerror(errno));
            }
      if (policy != SCHED_FIFO)
            printf("midi thread %d _NOT_ running SCHED_FIFO\n", getpid());
      updatePollFd();
      }

//---------------------------------------------------------
//   alsaMidiRead
//---------------------------------------------------------

static void alsaMidiRead(void*, void*)
      {
      // calls itself midiDevice->recordEvent(MidiRecordEvent):
      alsaProcessMidiInput();
      }

//---------------------------------------------------------
//   midiRead
//---------------------------------------------------------

static void midiRead(void*, void* d)
      {
      MidiDevice* dev = (MidiDevice*) d;
      dev->processInput();
      }

//---------------------------------------------------------
//   synthIRead
//---------------------------------------------------------

#if 0
static void synthIRead(void*, void* d)
      {
      SynthI* syn = (SynthI*) d;
      syn->processInput();
      }
#endif

//---------------------------------------------------------
//   midiWrite
//---------------------------------------------------------

static void midiWrite(void*, void* d)
      {
      MidiDevice* dev = (MidiDevice*) d;
      dev->flush();
      }

//---------------------------------------------------------
//   updatePollFd
//---------------------------------------------------------

void MidiSeq::updatePollFd()
      {
      if (!isRunning())
            return;

      clearPollFd();
      addPollFd(timerFd, POLLIN, midiTick, this, 0);

      if (timerFd == -1) {
            fprintf(stderr, "updatePollFd: no timer fd\n");
            if (!MusEGlobal::debugMode)
                  exit(-1);
            }

      addPollFd(toThreadFdr, POLLIN, ::readMsg, this, 0);

      //---------------------------------------------------
      //  midi ports
      //---------------------------------------------------

      for (iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) {
            MidiDevice* dev = *imd;
            int port = dev->midiPort();
            //const QString name = dev->name();
            if (port == -1)
                  continue;
            if ((dev->rwFlags() & 0x2) || (extSyncFlag.value()
               //&& (rxSyncPort == port || rxSyncPort == -1))) {
               //&& (dev->syncInfo().MCIn()))) {
               && (midiPorts[port].syncInfo().MCIn()))) {
                  if(dev->selectRfd() < 0){
                    //fprintf(stderr, "WARNING: read-file-descriptor for {%s} is negative\n", name.toLatin1());
                  }
                  addPollFd(dev->selectRfd(), POLLIN, ::midiRead, this, dev);
                  }
            if (dev->bytesToWrite()){
                  if(dev->selectWfd() < 0){
                    //fprintf(stderr, "WARNING: write-file-descriptor for {%s} is negative\n", name.toLatin1());
                  }
                  addPollFd(dev->selectWfd(), POLLOUT, ::midiWrite, this, dev);
            }      
            }
      // special handling for alsa midi:
      // (one fd for all devices)
      //    this allows for processing of some alsa events
      //    even if no alsa driver is active (assigned to a port)
      addPollFd(alsaSelectRfd(), POLLIN, ::alsaMidiRead, this, 0);
      }

//---------------------------------------------------------
//   threadStop
//    called from loop()
//---------------------------------------------------------

void MidiSeq::threadStop()
      {
      timer->stopTimer();
      //timer.stopTimer();
      }

//---------------------------------------------------------
//   setRtcTicks
//    return true on success
//---------------------------------------------------------

bool MidiSeq::setRtcTicks()
      {

      //timer.setTimerFreq(MusEConfig::config.rtcTicks);
      //timer.startTimer();
      timer->setTimerFreq(MusEConfig::config.rtcTicks);
      timer->startTimer();
      realRtcTicks = MusEConfig::config.rtcTicks;
      return true;
      }

//---------------------------------------------------------
//   start
//    return true on error
//---------------------------------------------------------

//bool MidiSeq::start()
void MidiSeq::start(int priority)
      {
      // Changed by Tim. p3.3.17
      
      prio = priority;
      
      //timerFd = -1;

      MusEGlobal::doSetuid();
      //timerFd = selectTimer(); 
      //timerFd = timer.initTimer();
      //printf("timerFd=%d\n",timerFd);
      setRtcTicks();
      MusEGlobal::undoSetuid();
      //Thread::start();
      Thread::start(priority);
      //return false;
      }

//---------------------------------------------------------
//   processMidiClock
//---------------------------------------------------------

void MidiSeq::processMidiClock()
      {
//      if (genMCSync) {
//            midiPorts[txSyncPort].sendClock();
//      }

/*      if (state == START_PLAY) {
            // start play on sync
            state      = PLAY;
            _midiTick  = playTickPos;
            midiClock  = playTickPos;

            int bar, beat, tick;
            sigmap.tickValues(_midiTick, &bar, &beat, &tick);
            midiClick      = sigmap.bar2tick(bar, beat+1, 0);

            double cpos    = tempomap.tick2time(playTickPos);
            samplePosStart = samplePos - lrint(cpos * MusEGlobal::sampleRate);
            rtcTickStart   = rtcTick - lrint(cpos * realRtcTicks);

            endSlice       = playTickPos;
            recTick        = playTickPos;
            lastTickPos    = playTickPos;

            tempoSN = tempomap.tempoSN();

            startRecordPos.setPosTick(playTickPos);
            }
*/
//      midiClock += MusEConfig::config.division/24;
      }

//---------------------------------------------------------
//   midiTick
//---------------------------------------------------------

void MidiSeq::midiTick(void* p, void*)
      {
      MidiSeq* at = (MidiSeq*)p;
      at->processTimerTick();
      if (TIMER_DEBUG)
      {
        if(MidiSeq::ticker++ > 100)
          {
          printf("tick!\n");
          MidiSeq::ticker=0;
          }
        }
      }

//---------------------------------------------------------
//   processTimerTick
//---------------------------------------------------------

void MidiSeq::processTimerTick()
      {
      // Disabled by Tim. p3.3.22
//      extern int watchMidi;
//      ++watchMidi;      // make a simple watchdog happy

      //---------------------------------------------------
      //    read elapsed rtc timer ticks
      //---------------------------------------------------

      // This is required otherwise it freezes.
      unsigned long nn;
      if (timerFd != -1) {
            nn = timer->getTimerTicks();
            //nn = timer.getTimerTicks();
            nn >>= 8;
            }

      if (idle) {
//        printf("IDLE\n");
            return;
      }
      if (midiBusy) {
            // we hit audio: midiSeq->msgProcess (actually this has been audio->processMidi for some time now - Tim)
            // miss this timer tick
            return;
            }

      unsigned curFrame = audio->curFrame();
      
      // Keep the sync detectors running... 
      // No, done in Song::beat(), (at the much slower heartbeat rate).
      //
      //for(int port = 0; port < MIDI_PORTS; ++port)
      //{
        // Must keep them running even if there's no device...
        //if(midiPorts[port].device())
      //    midiPorts[port].syncInfo().setCurFrame(curFrame);
      //}  
      //for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) 
      //  (*imd)->syncInfo().setCurFrame(curFrame);
      
      if (!extSyncFlag.value()) {
            //int curTick = tempomap.frame2tick(curFrame);
            // Copied from Tempomap.
            //int curTick = lrint((double(curFrame)/double(MusEGlobal::sampleRate)) * tempomap.globalTempo() * MusEConfig::config.division * 10000.0 / double(tempomap.tempo(song->cpos())));
            //int curTick = lrint((double(curFrame)/double(MusEGlobal::sampleRate)) * tempomap.globalTempo() * 240000.0 / double(tempomap.tempo(song->cpos())));
            int curTick = lrint((double(curFrame)/double(MusEGlobal::sampleRate)) * double(tempomap.globalTempo()) * double(MusEConfig::config.division) * 10000.0 / double(tempomap.tempo(song->cpos())));
            //int curTick = int((double(curFrame)/double(MusEGlobal::sampleRate)) * double(tempomap.globalTempo()) * double(MusEConfig::config.division * 10000.0) / double(tempomap.tempo(song->cpos())));
            
/*            if ( midiClock > curTick + 100) // reinitialize
                {
              midiClock = curTick;
                }
            else if( curTick > midiClock + 100) // reinitialize
                {
              midiClock = curTick;
                }*/
              
            if(midiClock > curTick)
              midiClock = curTick;
            
            int div = MusEConfig::config.division/24;
            if(curTick >= midiClock + div)  {
            //if(curTick >= midiClock)  {
                  //processMidiClock();
                  int perr = (curTick - midiClock) / div;
                  //int perr = curTick - midiClock;
                  
                  bool used = false;
                  
                  //if(genMCSync)
                  //{ 
                    //midiPorts[txSyncPort].sendClock();
                    for(int port = 0; port < MIDI_PORTS; ++port)
                    {
                      MidiPort* mp = &midiPorts[port];
                      
                      // No device? Clock out not turned on?
                      //MidiDevice* dev = mp->device();
                      //if(!dev || !mp->syncInfo().MCOut())
                      if(!mp->device() || !mp->syncInfo().MCOut())
                        continue;
                        
                      // Shall we check open flags?
                      //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1))
                      //if(!(dev->openFlags() & 1))
                      //  continue;
        
                      used = true;
                      
                      mp->sendClock();
                    }
                    
                    /*
                    for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) 
                    {
                      MidiDevice* dev = *imd;
                      
                      if(!dev->syncInfo().MCOut())
                        continue;
                        
                      // Shall we check open flags?
                      //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1))
                      //if(!(dev->openFlags() & 1))
                      //  continue;
        
                      int port = dev->midiPort();
                      // Without this -1 check, interesting sync things can be done by the user without ever
                      //  assigning any devices to ports ! 
                      //if(port < 0 || port > MIDI_PORTS)
                      if(port < -1 || port > MIDI_PORTS)
                        continue;
                        
                      if(port == -1)
                      // Send straight to the device... Copied from MidiPort.
                      {
                        MidiPlayEvent event(0, 0, 0, ME_CLOCK, 0, 0);
                        dev->putEvent(event);
                      }  
                      else
                        // Go through the port...
                        midiPorts[port].sendClock();
                    }
                    */
                    
                    if(MusEGlobal::debugMsg && used && perr > 1)
                      printf("Dropped %d midi out clock(s). curTick:%d midiClock:%d div:%d\n", perr, curTick, midiClock, div);
                  //}
                    
                  // Increment as if we had caught the timer exactly on the mark, even if the timer
                  //  has passed beyond the mark, or even beyond 2 * div.
                  // If we missed some chances to send clock, resume the count where it would have been, 
                  //  had we not missed chances.
                  // We can't do anything about missed chances except send right away, and make up
                  //  for gained time by losing time in the next count...
                  // In other words, use equalization periods to counter gained/lost time, so that
                  //  ultimately, over time, the receiver remains in phase, despite any short dropouts / phase glitches.
                  // (midiClock only increments by div units).
                  //
                  // Tested: With midi thread set to high priority, very few clock dropouts ocurred (P4 1.6Ghz).
                  // But target device tick drifts out of phase with muse tick slowly over time, say 20 bars or so.
                  // May need more tweaking, possibly use round with/instead of lrint (above), and/or
                  //  do not use equalization periods - set midiClock to fractions of div.
                  // Tested: With RTC resolution at 1024, stability was actually better than with 8192!
                  // It stayed in sync more than 64 bars...
                  //
                  //
                  // Using equalization periods...
                  midiClock += (perr * div);
                  //midiClock += perr;
                  //
                  // No equalization periods... TODO:
                  //midiClock += (perr * div);
               }
            }

//      if (genMTCSync) {
            // printf("Midi Time Code Sync generation not impl.\n");
//            }

      // p3.3.25
      //int tickpos = audio->tickPos();
      //bool extsync = extSyncFlag.value();
      //
      // play all events upto curFrame
      //
      for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) {
            //MidiDevice* md = *id;
            // Is it a Jack midi device? They are iterated in Audio::processMidi. p3.3.36 
            //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md);
            //if(mjd)
            //if(md->deviceType() == MidiDevice::JACK_MIDI)
            //  continue;
            //if(md->isSynti())      // syntis are handled by audio thread
            //      continue;
            // Only ALSA midi devices are handled by this thread.
            if((*id)->deviceType() == MidiDevice::ALSA_MIDI)
              (*id)->processMidi();
            
            // Moved into MidiAlsaDevice.      p4.0.34
            /*
            int port = md->midiPort();
            MidiPort* mp = port != -1 ? &midiPorts[port] : 0;
            MPEventList* el = md->playEvents();
            if (el->empty())
                  continue;
            
            ///iMPEvent i = md->nextPlayEvent();
            iMPEvent i = el->begin();            // p4.0.15 Tim.
            
            for (; i != el->end(); ++i) {
                  // p3.3.25
                  // If syncing to external midi sync, we cannot use the tempo map.
                  // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames.
                  //if (i->time() > curFrame) {
                  if (i->time() > (extsync ? tickpos : curFrame)) {
                        //printf("  curT %d  frame %d\n", i->time(), curFrame);
                        break; // skip this event
                        }

                  if (mp) {
                        if (mp->sendEvent(*i))
                              break;
                        }
                  else {
                        if (md->putEvent(*i))
                              break;
                        }
                  }
            ///md->setNextPlayEvent(i);
            // p4.0.15 We are done with these events. Let us erase them here instead of Audio::processMidi.
            // That way we can simply set the next play event to the beginning.
            // This also allows other events to be inserted without the problems caused by the next play event 
            //  being at the 'end' iterator and not being *easily* set to some new place beginning of the newer insertions. 
            // The way that MPEventList sorts made it difficult to predict where the iterator of the first newly inserted items was.
            // The erasure in Audio::processMidi was missing some events because of that.
            el->erase(el->begin(), i);
            //md->setNextPlayEvent(el->begin());  // Removed p4.0.15
            */
            
            }
      }

//---------------------------------------------------------
//   msgMsg
//---------------------------------------------------------

void MidiSeq::msgMsg(int id)
      {
      AudioMsg msg;
      msg.id = id;
      Thread::sendMsg(&msg);
      }

//---------------------------------------------------------
//   msgSetMidiDevice
//    to avoid timeouts in the RT-thread, setMidiDevice
//    is done in GUI context after setting the midi thread
//    into idle mode
//---------------------------------------------------------

void MidiSeq::msgSetMidiDevice(MidiPort* port, MidiDevice* device)
      {
      AudioMsg msg;
      msg.id = SEQM_IDLE;
      msg.a  = true;
      Thread::sendMsg(&msg);

      port->setMidiDevice(device);

      msg.id = SEQM_IDLE;
      msg.a  = false;
      Thread::sendMsg(&msg);
      }

// This does not appear to be used anymore. Was called in Audio::process1, now Audio::processMidi is called directly. p4.0.15 Tim.
//void MidiSeq::msgProcess()      { msgMsg(MS_PROCESS); }
//void MidiSeq::msgSeek()         { msgMsg(SEQM_SEEK); }   // Removed p4.0.34
//void MidiSeq::msgStop()         { msgMsg(MS_STOP); }     //
void MidiSeq::msgSetRtc()       { msgMsg(MS_SET_RTC); }
void MidiSeq::msgUpdatePollFd() { msgMsg(MS_UPDATE_POLL_FD); }