diff options
| -rw-r--r-- | muse2/ChangeLog | 16 | ||||
| -rw-r--r-- | muse2/README.effects-rack | 11 | ||||
| -rw-r--r-- | muse2/muse/audio.cpp | 542 | ||||
| -rw-r--r-- | muse2/muse/audio.h | 8 | ||||
| -rw-r--r-- | muse2/muse/driver/alsamidi.cpp | 369 | ||||
| -rw-r--r-- | muse2/muse/driver/alsamidi.h | 20 | ||||
| -rw-r--r-- | muse2/muse/driver/jackmidi.cpp | 851 | ||||
| -rw-r--r-- | muse2/muse/driver/jackmidi.h | 89 | ||||
| -rw-r--r-- | muse2/muse/instruments/minstrument.cpp | 1 | ||||
| -rw-r--r-- | muse2/muse/midi.cpp | 756 | ||||
| -rw-r--r-- | muse2/muse/midi.h | 2 | ||||
| -rw-r--r-- | muse2/muse/mididev.cpp | 29 | ||||
| -rw-r--r-- | muse2/muse/mididev.h | 22 | ||||
| -rw-r--r-- | muse2/muse/midiseq.cpp | 84 | ||||
| -rw-r--r-- | muse2/muse/midiseq.h | 8 | ||||
| -rw-r--r-- | muse2/muse/mpevent.h | 4 | ||||
| -rw-r--r-- | muse2/muse/song.cpp | 22 | ||||
| -rw-r--r-- | muse2/muse/synth.cpp | 28 | ||||
| -rw-r--r-- | muse2/muse/synth.h | 1 | 
19 files changed, 719 insertions, 2144 deletions
diff --git a/muse2/ChangeLog b/muse2/ChangeLog index bdc4cb57..b0bc5121 100644 --- a/muse2/ChangeLog +++ b/muse2/ChangeLog @@ -1,3 +1,19 @@ +09.09.2011: +        - Removed sending of SEQM_ADD_TRACK, SEQM_REMOVE_TRACK, SEQM_CHANGE_TRACK, SEQM_REMOVE_PART, and  +           SEQM_CHANGE_PART to ALSA midi thread (which waits) from inside Audio::processMsg. +          Replaced with hand-offs to Song::processMsg. TODO: SEQM_SET_TRACK_OUT_CHAN etc. (Tim) +        - MidiDevice play and stuck event lists no longer directly accessible from outside - +           replaced with ::addScheduledEvent and ::addStuckNote. (Tim) +        - Installed a ring buffer for ALSA MidiDevice play/stuck notes event lists, filled by ::addScheduledEvent  +           and ::addStuckNote, because ALSA midi is processed in a thread other than audio. (Tim) +          TODO: processMidi could do stuck notes better by bypassing their insertion in the play event list, +                 also applies to seek/stop handler block, but I'm not sure about the time comparisons.    +        - Some more separation of ALSA and Jack Midi, one step closer to configurable choice of either/or. (Tim) +        - Massive cleanup of audio, midi, mididev, alsamidi, jackmidi modules. Moved some stuff from Audio::processMidi  +           into MidiDevice. (Tim) +        - Test OK here so far, with stress files and complete pre-existing songs, ALSA, Jack, and Synth midi. (Tim) +          TODO: Restoration of sustain upon play seems broken for a while - try putEvent instead of addScheduledEvent +           in Audio::startRolling().  08.09.2011:          - Finished namespace MusEWidget. Added namespaces MusEUtil, MusEDialog. (Orcan)  07.09.2011: diff --git a/muse2/README.effects-rack b/muse2/README.effects-rack index 823ae74c..071ba08e 100644 --- a/muse2/README.effects-rack +++ b/muse2/README.effects-rack @@ -1,4 +1,4 @@ -May 6, 2010 v0.0.2 by Tim. +Sept 1, 2011 v0.0.3 by Tim.  Understanding the Effects Rack  ------------------------------ @@ -48,15 +48,12 @@ These same rules apply to inter-plugin audio when more than one plugin   is in the rack chain. Extra audio outputs of one plugin may be ignored   by the next plugin if not used.  -Currently plugins with no audio outputs are not really useful in MusE, - chiefly because plugin control outputs are not used or displayed. -Nor are specialized plugins with many inputs and/or outputs. +Currently specialized plugins with many inputs and/or outputs are not  + really useful in MusE.  Nor are so-called 'realtime' control plugins which use audio inputs    and outputs for control signals.  -<<<  - Loud noise alert! Beware of using such plugins in an audio effects rack.  ->>> +Loud noise alert! Beware of using such plugins in an audio effects rack.   Example: Consider a stereo Audio Input track with these effect rack  diff --git a/muse2/muse/audio.cpp b/muse2/muse/audio.cpp index a82fbe13..60eb5c73 100644 --- a/muse2/muse/audio.cpp +++ b/muse2/muse/audio.cpp @@ -4,6 +4,7 @@  //  $Id: audio.cpp,v 1.59.2.30 2009/12/20 05:00:35 terminator356 Exp $  //  //  (C) Copyright 2001-2004 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -30,10 +31,8 @@  #include "song.h"  #include "node.h"  #include "audiodev.h" -//#include "driver/audiodev.h"   // p4.0.2  #include "mididev.h"  #include "alsamidi.h" -//#include "driver/alsamidi.h"   // p4.0.2  #include "synth.h"  #include "audioprefetch.h"  #include "plugin.h" @@ -55,8 +54,7 @@ extern double curTime();  Audio* audio;  AudioDevice* audioDevice;   // current audio device in use -// p3.3.25 -extern unsigned int volatile midiExtSyncTicks; +extern unsigned int volatile midiExtSyncTicks;   // p3.3.25  //static const unsigned char mmcDeferredPlayMsg[] = { 0x7f, 0x7f, 0x06, 0x03 }; @@ -126,7 +124,7 @@ Audio::Audio()        _pos.setType(Pos::FRAMES);        _pos.setFrame(0); -      curTickPos    = 0; +      nextTickPos = curTickPos = 0;        midiClick     = 0;        clickno       = 0; @@ -140,10 +138,9 @@ Audio::Audio()        state         = STOP;        msg           = 0; -      // Changed by Tim. p3.3.8        //startRecordPos.setType(Pos::TICKS);        //endRecordPos.setType(Pos::TICKS); -      startRecordPos.setType(Pos::FRAMES); +      startRecordPos.setType(Pos::FRAMES);  // Tim        endRecordPos.setType(Pos::FRAMES);        _audioMonitor = 0; @@ -187,16 +184,12 @@ bool Audio::start()        _loopCount = 0;        MusEGlobal::muse->setHeartBeat();        if (audioDevice) { -          // Added by Tim. p3.3.6            //_running = true; -                      //audioDevice->start();            }        else {            if(false == initJackAudio()) { -                // Added by Tim. p3.3.6                  //_running = true; -                                  InputList* itl = song->inputs();                  for (iAudioInput i = itl->begin(); i != itl->end(); ++i) {                        //printf("reconnecting input %s\n", (*i)->name().ascii()); @@ -253,25 +246,6 @@ void Audio::stop(bool)  bool Audio::sync(int jackState, unsigned frame)        { -       -// Changed by Tim. p3.3.24 -/*       -      bool done = true; -      if (state == LOOP1)  -            state = LOOP2; -      else { -            if (_pos.frame() != frame) { -                  Pos p(frame, false); -                  seek(p); -                  } -            state = State(jackState); -            if (!_freewheel) -                  //done = audioPrefetch->seekDone; -                  done = audioPrefetch->seekDone(); -            } -       -      return done; -*/              bool done = true;        if (state == LOOP1)              state = LOOP2; @@ -283,8 +257,6 @@ bool Audio::sync(int jackState, unsigned frame)              //  PLAY -> START_PLAY  seek in play state              if (state != START_PLAY) { -                //Pos p(frame, AL::FRAMES); -                //    seek(p);                  Pos p(frame, false);                  seek(p);                if (!_freewheel) @@ -293,10 +265,8 @@ bool Audio::sync(int jackState, unsigned frame)                          state = START_PLAY;                  }              else { -                //if (frame != _seqTime.pos.frame()) {                  if (frame != _pos.frame()) {                          // seek during seek -                            //seek(Pos(frame, AL::FRAMES));                              seek(Pos(frame, false));                          }                  done = audioPrefetch->seekDone(); @@ -335,9 +305,8 @@ void Audio::shutdown()  void Audio::process(unsigned frames)        { -      // Disabled by Tim. p3.3.22  //      extern int watchAudio; -//      ++watchAudio;           // make a simple watchdog happy +//      ++watchAudio;           // make a simple watchdog happy. Disabled.         if (!MusEGlobal::checkAudioDevice()) return;        if (msg) { @@ -377,8 +346,8 @@ void Audio::process(unsigned frames)              startRolling();              }        else if (isPlaying() && jackState == STOP) { -            // p3.3.43 Make sure to stop bounce and freewheel mode, for example if user presses stop  -            //  in QJackCtl before right-hand marker is reached (which is handled below). +            // Make sure to stop bounce and freewheel mode, for example if user presses stop  +            //  in QJackCtl before right-hand marker is reached (which is handled below). p3.3.43               //printf("Audio::process isPlaying() && jackState == STOP\n");              //if (_bounce)               //{ @@ -485,15 +454,11 @@ void Audio::process(unsigned frames)                          //audioDevice->seekTransport(_loopFrame);                          Pos lp(_loopFrame, false);                          audioDevice->seekTransport(lp); - -  // printf("  process: seek to %d, end %d\n", _loopFrame, loop.frame());                          }                    } -             -            // p3.3.25 -            if(extSyncFlag.value()) +            if(extSyncFlag.value())        // p3.3.25              {                nextTickPos = curTickPos + midiExtSyncTicks;                // Probably not good - interfere with midi thread. @@ -549,7 +514,6 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames)            continue;          track = (AudioTrack*)(*it); -        // Added by T356.          // For audio track types, synths etc. which need some kind of non-audio           //  (but possibly audio-affecting) processing always, even if their output path          //  is ultimately unconnected. @@ -560,23 +524,10 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames)          // It should be used for things like midi events, gui events etc. - things which need to          //  be done BEFORE all the AudioOutput::process() are called below. That does NOT include           //  audio processing, because THAT is done at the very end of this routine. -        // This will also reset the track's processed flag. +        // This will also reset the track's processed flag.         Tim.          track->preProcessAlways(); -         -        // Removed by T356 -        /* -        if (track->noOutRoute() && !track->noInRoute() &&  -            track->type() != Track::AUDIO_AUX && track->type() != Track::AUDIO_OUTPUT) { -              channels = track->channels(); -              float* buffer[channels]; -              float data[frames * channels]; -              for (int i = 0; i < channels; ++i) -                    buffer[i] = data + i * frames; -              track->copyData(samplePos, channels, frames, buffer); -              } -        */ -                          } +              // Pre-process the metronome.        ((AudioTrack*)metronome)->preProcessAlways(); @@ -584,29 +535,12 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames)        for (ciAudioOutput i = ol->begin(); i != ol->end(); ++i)           (*i)->process(samplePos, offset, frames); -      // Removed by T356 -      /* -      AuxList* auxl = song->auxs(); -      for (ciAudioAux ia = auxl->begin(); ia != auxl->end(); ++ia) { -            track = (AudioTrack*)(*ia); -            if (track->noOutRoute()) { -                  channels = track->channels(); -                  float* buffer[channels]; -                  float data[frames * channels]; -                  for (int i = 0; i < channels; ++i) -                        buffer[i] = data + i * frames; -                  track->copyData(samplePos, channels, frames, buffer); -                  } -            } -      */       -             -      // Added by T356.        // Were ANY tracks unprocessed as a result of processing all the AudioOutputs, above?         // Not just unconnected ones, as previously done, but ones whose output path ultimately leads nowhere.        // Those tracks were missed, until this fix.        // Do them now. This will animate meters, and 'quietly' process some audio which needs to be done - -      //  for example synths really need to be processed, 'quietly' or not, otherwise the next time -      //  processing is 'turned on', if there was a backlog of events while it was off, then they all happen at once.  +      //  for example synths really need to be processed, 'quietly' or not, otherwise the next time processing  +      //  is 'turned on', if there was a backlog of events while it was off, then they all happen at once.  Tim.        for(ciTrack it = tl->begin(); it != tl->end(); ++it)         {          if((*it)->isMidiTrack()) @@ -623,9 +557,6 @@ void Audio::process1(unsigned samplePos, unsigned offset, unsigned frames)            for (int i = 0; i < channels; ++i)                  buffer[i] = data + i * frames;            //printf("Audio::process1 calling track->copyData for track:%s\n", track->name().toLatin1()); -       -          // p3.3.38 -          //track->copyData(samplePos, channels, frames, buffer);            track->copyData(samplePos, channels, -1, -1, frames, buffer);          }        }       @@ -647,7 +578,7 @@ void Audio::processMsg(AudioMsg* msg)              case AUDIO_ROUTEREMOVE:                    removeRoute(msg->sroute, msg->droute);                    break; -            case AUDIO_REMOVEROUTES:      // p3.3.55 +            case AUDIO_REMOVEROUTES:                          removeAllRoutes(msg->sroute, msg->droute);                    break;              //case AUDIO_VOL: @@ -670,7 +601,6 @@ void Audio::processMsg(AudioMsg* msg)                    break;              //case AUDIO_SET_PLUGIN_CTRL_VAL:                    //msg->plugin->track()->setPluginCtrlVal(msg->ival, msg->dval); -                  // p3.3.43              //      msg->snode->setPluginCtrlVal(msg->ival, msg->dval);              //      break;              case AUDIO_SWAP_CONTROLLER_IDX: @@ -721,8 +651,8 @@ void Audio::processMsg(AudioMsg* msg)                    //printf("Audio::processMsg SEQM_RESET_DEVICES\n");                      for (int i = 0; i < MIDI_PORTS; ++i)                                             {       -                    if(!midiPorts[i].device()) continue;                        // p4.0.15 -                    midiPorts[i].instrument()->reset(i, song->mtype()); +                    if(midiPorts[i].device())                        +                      midiPorts[i].instrument()->reset(i, song->mtype());                    }                          break;              case SEQM_INIT_DEVICES: @@ -759,7 +689,7 @@ void Audio::processMsg(AudioMsg* msg)              case MIDI_SHOW_INSTR_GUI:                    midiSeq->msgUpdatePollFd();                    break; -            case MIDI_SHOW_INSTR_NATIVE_GUI:   // p4.0.20 +            case MIDI_SHOW_INSTR_NATIVE_GUI:                       midiSeq->msgUpdatePollFd();                    break;              case SEQM_ADD_TEMPO: @@ -776,12 +706,12 @@ void Audio::processMsg(AudioMsg* msg)                          frameOffset   = syncFrame - samplePos;                          }                    break; -            case SEQM_ADD_TRACK: -            case SEQM_REMOVE_TRACK: -            case SEQM_CHANGE_TRACK: -            case SEQM_ADD_PART: -            case SEQM_REMOVE_PART: -            case SEQM_CHANGE_PART: +            //case SEQM_ADD_TRACK: +            //case SEQM_REMOVE_TRACK: +            //case SEQM_CHANGE_TRACK: +            //case SEQM_ADD_PART: +            //case SEQM_REMOVE_PART: +            //case SEQM_CHANGE_PART:              case SEQM_SET_TRACK_OUT_CHAN:              case SEQM_SET_TRACK_OUT_PORT:              case SEQM_REMAP_PORT_DRUM_CTL_EVS: @@ -813,143 +743,26 @@ void Audio::seek(const Pos& p)                printf("Audio::seek already there\n");              return;                      } -       -            // p3.3.23 -            //printf("Audio::seek frame:%d\n", p.frame()); +      //printf("Audio::seek frame:%d\n", p.frame());        _pos        = p;        if (!MusEGlobal::checkAudioDevice()) return;        syncFrame   = audioDevice->framePos();        frameOffset = syncFrame - _pos.frame();        curTickPos  = _pos.tick(); -      // p4.0.22 -      // Tell midi thread to tell ALSA devices to handle seek. -      midiSeq->msgSeek();      -      // We are in the audio thread. Directly seek Jack midi devices. +      if (curTickPos == 0 && !song->record())      +            audio->initDevices(); +        for(iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i)  -      { -        MidiDevice* md = *i; -        if(md->deviceType() == MidiDevice::JACK_MIDI)       -          md->handleSeek();   -      } -       -      // Moved into MidiDevice::handleSeek -      #if 0 -      // p3.3.31 -      // Don't send if external sync is on. The master, and our sync routing system will take care of that. -      if(!extSyncFlag.value()) -      { -         -        for(int port = 0; port < MIDI_PORTS; ++port)  -        { -          MidiPort* mp = &midiPorts[port]; -          MidiDevice* dev = mp->device(); -          //if(!dev || !mp->syncInfo().MCOut()) -          if(!dev || !mp->syncInfo().MRTOut()) -            continue; -             -          // Added by T356: Shall we check for device write open flag to see if it's ok to send?... -          // This means obey what the user has chosen for read/write in the midi port config dialog, -          //  which already takes into account whether the device is writable or not. -          //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) -          //if(!(dev->openFlags() & 1)) -          //  continue; -           -          //int port = dev->midiPort(); -           -          // By checking for no port here (-1), (and out of bounds), it means -          //  the device must be assigned to a port for these MMC commands to be sent. -          // Without this 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; -           -          int beat = (curTickPos * 4) / MusEConfig::config.division; -             -          bool isPlaying=false; -          if(state == PLAY) -            isPlaying = true; -             -          mp->sendStop(); -          mp->sendSongpos(beat); -          if(isPlaying) -            mp->sendContinue(); -        } -      } -      #endif   -         -      /* -      if(genMCSync)  -      { -        for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd)  -        { -          MidiDevice* dev = (*imd); -          if(!dev->syncInfo().MCOut()) -            continue; -             -          // Added by T356: Shall we check for device write open flag to see if it's ok to send?... -          // This means obey what the user has chosen for read/write in the midi port config dialog, -          //  which already takes into account whether the device is writable or not. -          //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) -          //if(!(dev->openFlags() & 1)) -          //  continue; -           -          int port = dev->midiPort(); -           -          // By checking for no port here (-1), (and out of bounds), it means -          //  the device must be assigned to a port for these MMC commands to be sent. -          // Without this 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; -           -          int beat = (curTickPos * 4) / MusEConfig::config.division; -             -          bool isPlaying=false; -          if(state == PLAY) -            isPlaying = true; -             -          if(port == -1) -          // Send straight to the device... Copied from MidiPort. -          { -            MidiPlayEvent event(0, 0, 0, ME_STOP, 0, 0); -            dev->putEvent(event); -             -            event.setType(ME_SONGPOS); -            event.setA(beat); -            dev->putEvent(event); -             -            if(isPlaying) -            { -              event.setType(ME_CONTINUE); -              event.setA(0); -              dev->putEvent(event); -            }   -          } -          else -          // Go through the port... -          { -            MidiPort* mp = &midiPorts[port]; -             -            mp->sendStop(); -            mp->sendSongpos(beat); -            if(isPlaying) -              mp->sendContinue(); -          } -        } -      } -      */ +          (*i)->handleSeek();          //loopPassed = true;   // for record loop mode        if (state != LOOP2 && !freewheel())        { -            // Changed by T356 08/17/08. We need to force prefetch to update, -            //  to ensure the most recent data. Things can happen to a part -            //  before play is pressed - such as part muting, part moving etc. -            // Without a force, the wrong data was being played.              //audioPrefetch->msgSeek(_pos.frame()); +            // We need to force prefetch to update, to ensure the most recent data.  +            // Things can happen to a part before play is pressed - such as part muting,  +            //  part moving etc. Without a force, the wrong data was being played.  Tim 08/17/08              audioPrefetch->msgSeek(_pos.frame(), true);        } @@ -984,8 +797,6 @@ void Audio::writeTick()  void Audio::startRolling()        { -      // Changed by Tim. p3.3.8 -      //startRecordPos = _pos;        if (MusEGlobal::debugMsg)          printf("startRolling - loopCount=%d, _pos=%d\n", _loopCount, _pos.tick()); @@ -1005,20 +816,9 @@ void Audio::startRolling()        state = PLAY;        write(sigFd, "1", 1);   // Play -      // p3.3.31        // Don't send if external sync is on. The master, and our sync routing system will take care of that.        if(!extSyncFlag.value())        { -         -        // Changed by Tim. p3.3.6 -        //if (genMMC) -        //    midiPorts[txSyncPort].sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); -        //if (genMCSync) { -        //      if (curTickPos) -        //            midiPorts[txSyncPort].sendContinue(); -        //      else -        //            midiPorts[txSyncPort].sendStart(); -        //      }          for(int port = 0; port < MIDI_PORTS; ++port)           {            MidiPort* mp = &midiPorts[port]; @@ -1033,13 +833,9 @@ void Audio::startRolling()            MidiSyncInfo& si = mp->syncInfo(); -          //if(genMMC && si.MMCOut())            if(si.MMCOut()) -            //mp->sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg));              mp->sendMMCDeferredPlay(); -          //if(genMCSync && si.MCOut()) -          //if(si.MCOut())            if(si.MRTOut())            {              if(curTickPos) @@ -1050,68 +846,6 @@ void Audio::startRolling()          }        }   -      /* -      for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd)  -      { -        MidiDevice* dev = (*imd); -           -        // 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; -         -        MidiSyncInfo& si = dev->syncInfo(); -           -        if(port == -1) -        // Send straight to the device... Copied from MidiPort. -        { -          if(genMMC && si.MMCOut()) -          { -            MidiPlayEvent event(0, 0, ME_SYSEX, mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); -            dev->putEvent(event); -          } -           -          if(genMCSync && si.MCOut()) -          { -            if(curTickPos) -            { -              MidiPlayEvent event(0, 0, 0, ME_CONTINUE, 0, 0); -              dev->putEvent(event); -            }   -            else -            { -              MidiPlayEvent event(0, 0, 0, ME_START, 0, 0); -              dev->putEvent(event); -            }   -          } -        } -        else -        // Go through the port... -        { -          MidiPort* mp = &midiPorts[port]; -             -          if(genMMC && si.MMCOut()) -            mp->sendSysex(mmcDeferredPlayMsg, sizeof(mmcDeferredPlayMsg)); -           -          if(genMCSync && si.MCOut()) -          { -            if(curTickPos) -              mp->sendContinue(); -            else -              mp->sendStart(); -          } -        }   -      } -      */ -              if (MusEGlobal::precountEnableFlag           && song->click()           && !extSyncFlag.value() @@ -1150,9 +884,7 @@ void Audio::startRolling()                    if(mp->device() != NULL) {                          //printf("send enable sustain!!!!!!!! port %d ch %d\n", i,ch);                          MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 127); -                         -                        // may cause problems, called from audio thread -                        mp->device()->playEvents()->add(ev); +                        mp->device()->addScheduledEvent(ev);    // TODO: Not working? Try putEvent                          }                    }                } @@ -1172,220 +904,14 @@ void Audio::stopRolling()        state = STOP; -      //playStateExt = false; // not playing   // Moved here from MidiSeq::processStop()   p4.0.22 - -      // p4.0.22 -      // Tell midi thread to clear ALSA device notes and stop stuck notes. -      midiSeq->msgStop();        -      // We are in the audio thread. Directly clear Jack midi device notes and stop stuck notes. +      midiSeq->setExternalPlayState(false); // not playing   Moved here from MidiSeq::processStop()   p4.0.34 +              for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id)         {          MidiDevice* md = *id; -        if(md->deviceType() == MidiDevice::JACK_MIDI)   -          md->handleStop(); +        md->handleStop();        } -      // Moved into MidiDevice::handleStop()  // p4.0.22 -      #if 0 //TODO -      //--------------------------------------------------- -      //    reset sustain -      //--------------------------------------------------- - - -      // clear sustain -      for (int i = 0; i < MIDI_PORTS; ++i) { -          MidiPort* mp = &midiPorts[i]; -          for (int ch = 0; ch < MIDI_CHANNELS; ++ch) { -              if (mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127) { -                  if(mp->device()!=NULL) { -                      //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); -                      MidiPlayEvent ev(0, i, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); -                      // may cause problems, called from audio thread -                      mp->device()->putEvent(ev); -                      } -                  } -              } -          } -      #endif -       -      // Moved into MidiDevice::handleStop()  // p4.0.22 -      #if 0 -      // p3.3.31 -      // Don't send if external sync is on. The master, and our sync routing system will take care of that. -      if(!extSyncFlag.value()) -      { -         -        // Changed by Tim. p3.3.6 -        //MidiPort* syncPort = &midiPorts[txSyncPort]; -        //if (genMMC) { -        //      unsigned char mmcPos[] = { -        //            0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, -        //            0, 0, 0, 0, 0 -        //            }; -        //      int frame = tempomap.tick2frame(curTickPos); -        //      MTC mtc(double(frame) / double(MusEGlobal::sampleRate)); -        //      mmcPos[6] = mtc.h() | (mtcType << 5); -        //      mmcPos[7] = mtc.m(); -        //      mmcPos[8] = mtc.s(); -        //      mmcPos[9] = mtc.f(); -        //      mmcPos[10] = mtc.sf(); -        //      syncPort->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); -        //      syncPort->sendSysex(mmcPos, sizeof(mmcPos)); -        //      } -        //if (genMCSync) {         // Midi Clock -              // send STOP and -              // "set song position pointer" -        //      syncPort->sendStop(); -        //      syncPort->sendSongpos(curTickPos * 4 / MusEConfig::config.division); -        //      } -        for(int port = 0; port < MIDI_PORTS; ++port)  -        { -          MidiPort* mp = &midiPorts[port]; -          MidiDevice* dev = mp->device(); -          if(!dev) -            continue; -               -          // Shall we check open flags? -          //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) -          //if(!(dev->openFlags() & 1)) -          //  continue; -           -          MidiSyncInfo& si = mp->syncInfo(); -             -          //if(genMMC && si.MMCOut()) -          if(si.MMCOut()) -          { -            //unsigned char mmcPos[] = { -            //      0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, -            //      0, 0, 0, 0, 0 -            //      }; -   -            // p3.3.31 -            /* -            int frame = tempomap.tick2frame(curTickPos); -            MTC mtc(double(frame) / double(MusEGlobal::sampleRate)); -            */           -             -            //mmcPos[6] = mtc.h() | (mtcType << 5); -            //mmcPos[7] = mtc.m(); -            //mmcPos[8] = mtc.s(); -            //mmcPos[9] = mtc.f(); -            //mmcPos[10] = mtc.sf(); -             -            //mp->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); -            mp->sendMMCStop(); -            //mp->sendSysex(mmcPos, sizeof(mmcPos)); -             -            // p3.3.31 -            // Added check of option send continue not start. -            // Hmm, is this required? Seems to make other devices unhappy. -            /* -            if(!si.sendContNotStart()) -              mp->sendMMCLocate(mtc.h() | (mtcType << 5),  -                              mtc.m(), mtc.s(), mtc.f(), mtc.sf()); -            */                   -             -          } -         -          //if(genMCSync && si.MCOut()) // Midi Clock -          //if(si.MCOut()) // Midi Clock -          if(si.MRTOut()) //  -          { -            // send STOP and -            // "set song position pointer" -            mp->sendStop(); -             -            // p3.3.31 -            // Added check of option send continue not start. -            // Hmm, is this required? Seems to make other devices unhappy. -            /* -            if(!si.sendContNotStart()) -              mp->sendSongpos(curTickPos * 4 / MusEConfig::config.division); -            */   -             -          } -        } -      } -      #endif -       -      /* -      for(iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd)  -      { -        MidiDevice* dev = (*imd); -           -        // 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; -         -        MidiSyncInfo& si = dev->syncInfo(); -           -        MidiPort* mp = 0; -        if(port != -1) -          mp = &midiPorts[port]; -         -        if(genMMC && si.MMCOut()) -        { -          unsigned char mmcPos[] = { -                0x7f, 0x7f, 0x06, 0x44, 0x06, 0x01, -                0, 0, 0, 0, 0 -                }; -          int frame = tempomap.tick2frame(curTickPos); -          MTC mtc(double(frame) / double(MusEGlobal::sampleRate)); -          mmcPos[6] = mtc.h() | (mtcType << 5); -          mmcPos[7] = mtc.m(); -          mmcPos[8] = mtc.s(); -          mmcPos[9] = mtc.f(); -          mmcPos[10] = mtc.sf(); -           -          if(mp) -          // Go through the port... -          { -            mp->sendSysex(mmcStopMsg, sizeof(mmcStopMsg)); -            mp->sendSysex(mmcPos, sizeof(mmcPos)); -          } -          else -          // Send straight to the device... Copied from MidiPort. -          { -            MidiPlayEvent event(0, 0, ME_SYSEX, mmcStopMsg, sizeof(mmcStopMsg)); -            dev->putEvent(event); -             -            event.setData(mmcPos, sizeof(mmcPos)); -            dev->putEvent(event); -          }   -        } -       -        if(genMCSync && si.MCOut()) // Midi Clock -        { -          // send STOP and -          // "set song position pointer" -          if(mp) -          // Go through the port... -          { -            mp->sendStop(); -            mp->sendSongpos(curTickPos * 4 / MusEConfig::config.division); -          } -          else -          // Send straight to the device... Copied from MidiPort. -          { -            MidiPlayEvent event(0, 0, 0, ME_STOP, 0, 0); -            dev->putEvent(event); -            event.setType(ME_SONGPOS); -            event.setA(curTickPos * 4 / MusEConfig::config.division); -            dev->putEvent(event); -          }  -        } -      } -      */       -                    WaveTrackList* tracks = song->waves();        for (iWaveTrack i = tracks->begin(); i != tracks->end(); ++i) {              WaveTrack* track = *i; diff --git a/muse2/muse/audio.h b/muse2/muse/audio.h index 86f38004..03e48e23 100644 --- a/muse2/muse/audio.h +++ b/muse2/muse/audio.h @@ -4,6 +4,7 @@  //  $Id: audio.h,v 1.25.2.13 2009/12/20 05:00:35 terminator356 Exp $  //  //  (C) Copyright 2001 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -239,8 +240,8 @@ class Audio {        bool sendMessage(AudioMsg* m, bool doUndo);        void msgRemoveRoute(Route, Route);        void msgRemoveRoute1(Route, Route); -      void msgRemoveRoutes(Route, Route);  // p3.3.55 -      void msgRemoveRoutes1(Route, Route);  // p3.3.55 +      void msgRemoveRoutes(Route, Route);   +      void msgRemoveRoutes1(Route, Route);          void msgAddRoute(Route, Route);        void msgAddRoute1(Route, Route);        void msgAddPlugin(AudioTrack*, int idx, PluginI* plugin); @@ -290,7 +291,8 @@ class Audio {        int loopCount() { return _loopCount; }         // Number of times we have looped so far        unsigned loopFrame() { return _loopFrame; }           -      int tickPos() const         { return curTickPos; } +      unsigned tickPos() const    { return curTickPos; } +      unsigned nextTick() const   { return nextTickPos; }        int timestamp() const;        void processMidi();        unsigned curFrame() const; diff --git a/muse2/muse/driver/alsamidi.cpp b/muse2/muse/driver/alsamidi.cpp index 07688a33..9a62dbdd 100644 --- a/muse2/muse/driver/alsamidi.cpp +++ b/muse2/muse/driver/alsamidi.cpp @@ -3,6 +3,7 @@  //  Linux Music Editor  //  $Id: alsamidi.cpp,v 1.8.2.7 2009/11/19 04:20:33 terminator356 Exp $  //  (C) Copyright 2000-2001 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -25,16 +26,18 @@  #include "alsamidi.h"  #include "globals.h"  #include "midi.h" -#include "mididev.h" +//#include "mididev.h"  #include "../midiport.h"  #include "../midiseq.h"  #include "../midictrl.h"  #include "../audio.h" -#include "mpevent.h" +//#include "mpevent.h"  //#include "sync.h"  #include "utils.h"  #include "audiodev.h"  #include "xml.h" +#include "part.h" +#include "gconfig.h"  static int alsaSeqFdi = -1;  static int alsaSeqFdo = -1; @@ -50,6 +53,8 @@ MidiAlsaDevice::MidiAlsaDevice(const snd_seq_addr_t& a, const QString& n)     : MidiDevice(n)        {        adr = a; +      stopPending = false;          +      seekPending = false;        init();        } @@ -132,7 +137,7 @@ void MidiAlsaDevice::close()        // Allocated on stack, no need to call snd_seq_port_subscribe_free() later.        snd_seq_port_subscribe_alloca(&subs); -      // Changed by T356. This function appears to be called only by MidiPort::setMidiDevice(),  +      // This function appears to be called only by MidiPort::setMidiDevice(),         //  which closes then opens the device.        // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags.        // @@ -146,7 +151,7 @@ void MidiAlsaDevice::close()        //                 dst 16:0        //                only sometimes (not when playing notes), but with jack midi turned on,         //                we don't get the messages. With jack stopped we get the messages -      //                no matter if jack midi is turned on or not. +      //                no matter if jack midi is turned on or not.   Tim.        //if (_openFlags & 1) {        //if (!(_openFlags & 1))  @@ -191,95 +196,27 @@ void MidiAlsaDevice::close()  void MidiAlsaDevice::writeRouting(int level, Xml& xml) const  { -      // p3.3.45        // If this device is not actually in use by the song, do not write any routes. -      // This prevents bogus routes from being saved and propagated in the med file. +      // This prevents bogus routes from being saved and propagated in the med file.  Tim.        if(midiPort() == -1)          return;        QString s; -      /* -      //if(rwFlags() & 2)  // Readable -      { -        //RouteList* rl = _inRoutes; -        //for (ciRoute r = rl->begin(); r != rl->end(); ++r)  -        for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r)  -        { -          // Since an ALSA midi device supports read + write, this is the only way we can tell if this route is using the device as input.   -          if(r->type == Route::TRACK_ROUTE) -            continue; -             -          if(!r->name().isEmpty()) -          { -            xml.tag(level++, "Route"); -             -            //xml.strTag(level, "srcNode", r->name()); -            xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); -             -            //xml.strTag(level, "dstNode", name()); -            xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().toLatin1().constData()); -             -            xml.etag(level--, "Route"); -          } -        }   -      }  -      */ -              for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r)         { -        //if(r->type != Route::TRACK_ROUTE) -        //{   -        //  printf("MidiAlsaDevice::writeRouting Warning out route is not TRACK_ROUTE type\n"); -        //  continue; -        //} -                    if(!r->name().isEmpty())          { -          //xml.tag(level++, "Route"); -                      s = QT_TRANSLATE_NOOP("@default", "Route");            if(r->channel != -1)              s += QString(QT_TRANSLATE_NOOP("@default", " channel=\"%1\"")).arg(r->channel);            xml.tag(level++, s.toLatin1().constData()); -           -          /* -          //xml.strTag(level, "srcNode", name()); -          if(r->channel != -1)   -            //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, r->channel, name().toLatin1().constData()); -            //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, r->channel, name().toLatin1().constData()); -            xml.tag(level, "source devtype=\"%d\" channel=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, r->channel, name().toLatin1().constData()); -          else   -            //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::ALSA_MIDI_ROUTE, name().toLatin1().constData()); -            //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().toLatin1().constData()); -          */   -            //xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, name().toLatin1().constData()); -            xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, Xml::xmlString(name()).toLatin1().constData()); -           -          /* -          //xml.strTag(level, "dstNode", r->name()); -          if(r->channel != -1)   -          { -            if(r->type == Route::MIDI_DEVICE_ROUTE)   -              xml.tag(level, "dest devtype=\"%d\" channel=\"%d\" name=\"%s\"/", r->device->deviceType(), r->channel, r->name().toLatin1().constData()); -            else   -              xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().toLatin1().constData()); -          } -          else   -          { -            if(r->type == Route::MIDI_DEVICE_ROUTE)   -              xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().toLatin1().constData()); -            else   -              xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); -          } -          */ -           +          xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, Xml::xmlString(name()).toLatin1().constData());            s = QT_TRANSLATE_NOOP("@default", "dest");            if(r->type == Route::MIDI_DEVICE_ROUTE)              s += QString(QT_TRANSLATE_NOOP("@default", " devtype=\"%1\"")).arg(r->device->deviceType());            else            if(r->type != Route::TRACK_ROUTE)              s += QString(QT_TRANSLATE_NOOP("@default", " type=\"%1\"")).arg(r->type); -          //s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(r->name());            s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(Xml::xmlString(r->name()));            xml.tag(level, s.toLatin1().constData()); @@ -326,12 +263,9 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e)                    int a   = e.dataA();                    int b   = e.dataB();                    int chn = e.channel(); -                  // p3.3.37 -                  //if (a < 0x1000) {          // 7 Bit Controller                    if (a < CTRL_14_OFFSET) {          // 7 Bit Controller                          snd_seq_ev_set_controller(&event, chn, a, b);                          } -                  //else if (a < 0x20000) {     // 14 bit high resolution controller                    else if (a < CTRL_RPN_OFFSET) {     // 14 bit high resolution controller                          int ctrlH = (a >> 8) & 0x7f;                          int ctrlL = a & 0x7f; @@ -339,7 +273,6 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e)                          snd_seq_ev_set_controller(&event, chn, a, b);                          event.type = SND_SEQ_EVENT_CONTROL14;                          } -                  //else if (a < 0x30000) {     // RPN 7-Bit Controller                    else if (a < CTRL_NRPN_OFFSET) {     // RPN 7-Bit Controller                          int ctrlH = (a >> 8) & 0x7f;                          int ctrlL = a & 0x7f; @@ -348,7 +281,6 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e)                          snd_seq_ev_set_controller(&event, chn, a, b);                          event.type = SND_SEQ_EVENT_REGPARAM;                          } -                  //else if (a < 0x40000) {     // NRPN 7-Bit Controller                    else if (a < CTRL_INTERNAL_OFFSET) {     // NRPN 7-Bit Controller                          int ctrlH = (a >> 8) & 0x7f;                          int ctrlL = a & 0x7f; @@ -357,7 +289,6 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e)                          snd_seq_ev_set_controller(&event, chn, a, b);                          event.type = SND_SEQ_EVENT_NONREGPARAM;                          } -                  //else if (a < 0x60000) {     // RPN14 Controller                    else if (a < CTRL_NRPN14_OFFSET) {     // RPN14 Controller                          int ctrlH = (a >> 8) & 0x7f;                          int ctrlL = a & 0x7f; @@ -365,7 +296,6 @@ bool MidiAlsaDevice::putMidiEvent(const MidiPlayEvent& e)                          snd_seq_ev_set_controller(&event, chn, a, b);                          event.type = SND_SEQ_EVENT_REGPARAM;                          } -                  //else if (a < 0x70000) {     // NRPN14 Controller                    else if (a < CTRL_NONE_OFFSET) {     // NRPN14 Controller                          int ctrlH = (a >> 8) & 0x7f;                          int ctrlL = a & 0x7f; @@ -466,6 +396,250 @@ bool MidiAlsaDevice::putEvent(snd_seq_event_t* event)        }  //--------------------------------------------------------- +//   processMidi +//   Called from ALSA midi sequencer thread only. +//--------------------------------------------------------- + +void MidiAlsaDevice::processMidi() +{ +  bool stop = stopPending;  // Snapshots +  bool seek = seekPending;  // +  seekPending = stopPending = false; +  // Transfer the stuck notes FIFO to the play events list. +  // FIXME It would be faster to have MidiAlsaDevice automatically add the stuck note so that only +  //  one FIFO would be needed. But that requires passing an extra 'tick' and 'off velocity' in +  //  addScheduledEvent, which felt too weird. +  while(!stuckNotesFifo.isEmpty()) +    _stuckNotes.add(stuckNotesFifo.get()); +     +  bool extsync = extSyncFlag.value(); +  //int frameOffset = getFrameOffset(); +  //int nextTick = audio->nextTick(); +   +  // We're in the ALSA midi thread. audio->isPlaying() might not be true during seek right now. +  //if(stop || (seek && audio->isPlaying())) +  if(stop || seek) +  { +    //--------------------------------------------------- +    //    Clear all notes and handle stuck notes +    //--------------------------------------------------- +    playEventFifo.clear(); +    _playEvents.clear(); +    //printf("transferring stuck notes\n");   +    for(iMPEvent i = _stuckNotes.begin(); i != _stuckNotes.end(); ++i)  +    { +      //printf(" stuck note\n");   +      MidiPlayEvent ev = *i; +      ev.setTime(0); +      _playEvents.add(ev); +    } +    _stuckNotes.clear(); +  } +  else +  { +    // Transfer the play events FIFO to the play events list. +    while(!playEventFifo.isEmpty()) +      _playEvents.add(playEventFifo.get()); +       +    /*  TODO Handle these more directly than putting them into play events list. +    //if(audio->isPlaying())   +    { +      iMPEvent k; +      for (k = _stuckNotes.begin(); k != _stuckNotes.end(); ++k) { +            if (k->time() >= nextTick)   +                  break; +            MidiPlayEvent ev(*k); +            if(extsync)              // p3.3.25 +              ev.setTime(k->time()); +            else  +              ev.setTime(tempomap.tick2frame(k->time()) + frameOffset); +            _playEvents.add(ev); +            } +      _stuckNotes.erase(_stuckNotes.begin(), k); +    } +    */ +    processStuckNotes();   +  } +   +  /* Instead, done immediately in handleStop and handleSeek using putEvent. +  if(stop) +  { +    // reset sustain... +    MidiPort* mp = &midiPorts[_port]; +    for(int ch = 0; ch < MIDI_CHANNELS; ++ch)  +    { +      if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127)  +      { +        //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); +        MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); +        _playEvents.add(ev); +      } +    } +  } +  if(seek) +  { +    // Send new contoller values... +    for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl)  +    { +      MidiCtrlValList* vl = ivl->second; +      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()) ) +          _playEvents.add(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); +      } +    } +  }    */ +   +  if (_playEvents.empty()) +        return; +   +  int port = midiPort(); +  MidiPort* mp = port != -1 ? &midiPorts[port] : 0; +  unsigned curFrame = audio->curFrame(); +  int tickpos = audio->tickPos(); +   +  // Play all events up to current frame. +  iMPEvent i = _playEvents.begin();             +  for (; i != _playEvents.end(); ++i) { +        if (i->time() > (extsync ? tickpos : curFrame))  // p3.3.25  Check: Should be nextTickPos? p4.0.34 +          break;  +        if(mp){ +          if (mp->sendEvent(*i)) +            break; +              } +        else  +          if(putMidiEvent(*i)) +            break; +        } +  _playEvents.erase(_playEvents.begin(), i); +} + +//--------------------------------------------------------- +//   handleStop +//--------------------------------------------------------- + +void MidiAlsaDevice::handleStop() +{ +  // If the device is not in use by a port, don't bother it. +  if(_port == -1) +    return; +     +  stopPending = true;  // Trigger stop handling in processMidi. +   +  //--------------------------------------------------- +  //    reset sustain +  //--------------------------------------------------- +   +  MidiPort* mp = &midiPorts[_port]; +  for(int ch = 0; ch < MIDI_CHANNELS; ++ch)  +  { +    if(mp->hwCtrlState(ch, CTRL_SUSTAIN) == 127)  +    { +      //printf("send clear sustain!!!!!!!! port %d ch %d\n", i,ch); +      MidiPlayEvent ev(0, _port, ch, ME_CONTROLLER, CTRL_SUSTAIN, 0); +      putMidiEvent(ev); +    } +  } +   +  //--------------------------------------------------- +  //    send midi stop +  //--------------------------------------------------- +   +  // Don't send if external sync is on. The master, and our sync routing system will take care of that.    +  if(!extSyncFlag.value()) +  { +    // Shall we check open flags? +    //if(!(dev->rwFlags() & 0x1) || !(dev->openFlags() & 1)) +    //if(!(dev->openFlags() & 1)) +    //  return; +           +    MidiSyncInfo& si = mp->syncInfo(); +    if(si.MMCOut()) +      mp->sendMMCStop(); +     +    if(si.MRTOut())  +    { +      // Send STOP  +      mp->sendStop(); +       +      // Added check of option send continue not start.    p3.3.31 +      // Hmm, is this required? Seems to make other devices unhappy. +      // (Could try now that this is in MidiDevice. p4.0.22 ) +      //if(!si.sendContNotStart()) +      //  mp->sendSongpos(audio->tickPos() * 4 / config.division); +    } +  }   +} + +//--------------------------------------------------------- +//   handleSeek +//--------------------------------------------------------- + +void MidiAlsaDevice::handleSeek() +{ +  // If the device is not in use by a port, don't bother it. +  if(_port == -1) +    return; +   +  seekPending = true;  // Trigger seek handling in processMidi. +   +  MidiPort* mp = &midiPorts[_port]; +  MidiCtrlValListList* cll = mp->controller(); +  int pos = audio->tickPos(); +   +  //--------------------------------------------------- +  //    Send new contoller values +  //--------------------------------------------------- +     +  for(iMidiCtrlValList ivl = cll->begin(); ivl != cll->end(); ++ivl)  +  { +    MidiCtrlValList* vl = ivl->second; +    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()) ) +        //_playEvents.add(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); +        // Hmm, play event list for immediate playback? Try putEvent instead.  p4.0.34  +        putMidiEvent(MidiPlayEvent(0, _port, ivl->first >> 24, ME_CONTROLLER, vl->num(), imcv->second.val)); +    } +  } +   +  //--------------------------------------------------- +  //    Send STOP and "set song position pointer" +  //--------------------------------------------------- +     +  // Don't send if external sync is on. The master, and our sync routing system will take care of that.  p3.3.31 +  if(!extSyncFlag.value()) +  { +    if(mp->syncInfo().MRTOut()) +    { +      // Shall we check for device write open flag to see if it's ok to send?... +      // This means obey what the user has chosen for read/write in the midi port config dialog, +      //  which already takes into account whether the device is writable or not. +      //if(!(rwFlags() & 0x1) || !(openFlags() & 1)) +      //if(!(openFlags() & 1)) +      //  continue; +       +      int beat = (pos * 4) / MusEConfig::config.division; +         +      //bool isPlaying = (state == PLAY); +      bool isPlaying = audio->isPlaying();  // TODO Check this it includes LOOP1 and LOOP2 besides PLAY.  p4.0.22 +         +      mp->sendStop(); +      mp->sendSongpos(beat); +      if(isPlaying) +        mp->sendContinue(); +    }     +  } +} + +//---------------------------------------------------------  //   initMidiAlsa  //    return true on error  //--------------------------------------------------------- @@ -539,7 +713,6 @@ bool initMidiAlsa()                    }              } -      // p3.3.38        //snd_seq_set_client_name(alsaSeq, "MusE Sequencer");        snd_seq_set_client_name(alsaSeq, audioDevice->clientName()); @@ -773,31 +946,6 @@ void alsaProcessMidiInput()                    return;                    } -            /* -            if(curPort == -1)  -            { -                if(mdev == 0) -                {   -                  if (MusEGlobal::debugMsg)  -                  { -                    fprintf(stderr, "no port %d:%d found for received alsa event\n", -                      ev->source.client, ev->source.port); -                  } -                } -                else -                { -                  // Allow the sync detect mechanisms to work, even if device is not assigned to a port. -                  if(ev->type == SND_SEQ_EVENT_CLOCK) -                    mdev->syncInfo().trigMCSyncDetect(); -                  else   -                  if(ev->type == SND_SEQ_EVENT_TICK) -                    mdev->syncInfo().trigTickDetect(); -                } -                snd_seq_free_event(ev); -                return; -            } -            */       -                                event.setType(0);      // mark as unused              event.setPort(curPort);              event.setB(0); @@ -867,7 +1015,6 @@ void alsaProcessMidiInput()                          break;                    case SND_SEQ_EVENT_SYSEX: -                                                  // TODO: Deal with large sysex, which are broken up into chunks!                          // For now, do not accept if the first byte is not SYSEX or the last byte is not EOX,                           //  meaning it's a chunk, possibly with more chunks to follow. @@ -916,13 +1063,7 @@ void alsaProcessMidiInput()                          break;              }              if(event.type()) -            {                mdev->recordEvent(event); -              // p3.3.26 1/23/10 Moved to MidiDevice now. Anticipating Jack midi support, so don't make it ALSA specific. Tim. -              //if(ev->type != SND_SEQ_EVENT_SYSEX) -                // Trigger general activity indicator detector. Sysex has no channel, don't trigger. -              //  midiPorts[curPort].syncInfo().trigActDetect(event.channel()); -            }              snd_seq_free_event(ev);              if (rv == 0) diff --git a/muse2/muse/driver/alsamidi.h b/muse2/muse/driver/alsamidi.h index 01894f8c..13e07ca4 100644 --- a/muse2/muse/driver/alsamidi.h +++ b/muse2/muse/driver/alsamidi.h @@ -3,6 +3,7 @@  //  Linux Music Editor  //  $Id: alsamidi.h,v 1.2 2004/01/14 09:06:43 wschweer Exp $  //  (C) Copyright 2001 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -26,6 +27,7 @@  #include <config.h>  #include <alsa/asoundlib.h> +#include "mpevent.h"  #include "mididev.h"  class Xml; @@ -39,6 +41,13 @@ class MidiAlsaDevice : public MidiDevice {        snd_seq_addr_t adr;     private: +      // Special for ALSA midi device: Play event list is processed in the ALSA midi sequencer thread. +      // Need this FIFO, to decouple from audio thread which adds events to the list.        +      MidiFifo playEventFifo;   +      MidiFifo stuckNotesFifo;   +      volatile bool stopPending;          +      volatile bool seekPending; +              virtual QString open();        virtual void close();        virtual void processInput()  {} @@ -49,17 +58,22 @@ class MidiAlsaDevice : public MidiDevice {        virtual bool putMidiEvent(const MidiPlayEvent&);     public: -      //MidiAlsaDevice() {}  // p3.3.55 Removed        MidiAlsaDevice(const snd_seq_addr_t&, const QString& name);        virtual ~MidiAlsaDevice() {} -      //virtual void* clientPort() { return (void*)&adr; } -      // p3.3.55        virtual void* inClientPort() { return (void*)&adr; }     // For ALSA midi, in/out client ports are the same.        virtual void* outClientPort() { return (void*)&adr; }    // That is, ALSA midi client ports can be both r/w.        virtual void writeRouting(int, Xml&) const;        virtual inline int deviceType() const { return ALSA_MIDI; }  +      // Schedule an event for playback. Returns false if event cannot be delivered. +      virtual bool addScheduledEvent(const MidiPlayEvent& ev) { return !playEventFifo.put(ev); } +      // Add a stuck note. Returns false if event cannot be delivered. +      virtual bool addStuckNote(const MidiPlayEvent& ev) { return !stuckNotesFifo.put(ev); } +      // Play all events up to current frame. +      virtual void processMidi(); +      virtual void handleStop(); +      virtual void handleSeek();        };  extern bool initMidiAlsa(); diff --git a/muse2/muse/driver/jackmidi.cpp b/muse2/muse/driver/jackmidi.cpp index 2e790a10..a4577f0b 100644 --- a/muse2/muse/driver/jackmidi.cpp +++ b/muse2/muse/driver/jackmidi.cpp @@ -3,6 +3,7 @@  //  Linux Music Editor  //  $Id: jackmidi.cpp,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $  //  (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -48,128 +49,16 @@  extern unsigned int volatile lastExtMidiSyncTick; -///int jackmidi_pi[2]; -///int jackmidi_po[2]; - -//extern muse_jack_midi_buffer jack_midi_out_data[JACK_MIDI_CHANNELS]; -//extern muse_jack_midi_buffer jack_midi_in_data[JACK_MIDI_CHANNELS]; -///extern jack_port_t *midi_port_in[JACK_MIDI_CHANNELS]; -///extern jack_port_t *midi_port_out[JACK_MIDI_CHANNELS]; - -///MidiJackDevice* gmdev = NULL; - -///int* jackSeq; -//static snd_seq_addr_t musePort; - -//int MidiJackDevice::_nextOutIdNum = 0; -//int MidiJackDevice::_nextInIdNum = 0; - -//int JackMidiPortList::_nextOutIdNum = 0; -//int JackMidiPortList::_nextInIdNum = 0; - -//JackMidiPortList jackMidiClientPorts; - - -/* -//--------------------------------------------------------- -//   JackMidiPortList -//--------------------------------------------------------- - -JackMidiPortList::JackMidiPortList() -{ - -} - -JackMidiPortList::~JackMidiPortList() -{ - -} - -iJackMidiPort JackMidiPortList::createClientPort(int flags) // 1 = writable, 2 = readable - do not mix -{ -  if(flags & 1) -  { -    char buf[80]; -    snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); -    jack_port_t* _client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); -    if(_client_jackport == NULL) -    { -      fprintf(stderr, "JackMidiPortList::createClientPort failed to register jack-midi-out\n"); -      //return QString("Could not register jack-midi-out client port"); -      return end(); -    } -    else -    { -      JackMidiPort jmp(_client_jackport, QString(buf), flags); -      _nextOutIdNum++; -      return insert(begin(), std::pair<jack_port_t*, JackMidiPort>(_client_jackport, jmp)); -    } -  } -  else  -  if(flags & 2) -  {   -    char buf[80]; -    snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); -    jack_port_t* _client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); -    if(_client_jackport == NULL) -    { -      fprintf(stderr, "JackMidiPortList::createClientPort failed to register jack-midi-in\n"); -      return end(); -    } -    else -    { -      JackMidiPort jmp(_client_jackport, QString(buf), flags); -      _nextInIdNum++; -      return insert(begin(), std::pair<jack_port_t*, JackMidiPort>(_client_jackport, jmp)); -    } -  } -  return end(); -} - -// Return true if removed. -bool JackMidiPortList::removeClientPort(jack_port_t* port)  -{ -  iJackMidiPort ijp = find(port); -  if(ijp == end()) -    return false; -     -  // Is output? -  if(ijp->second._flags & 1) -    _nextOutIdNum--; -  // Is input? -  if(ijp->second._flags & 2) -    _nextInIdNum--; -   -  erase(ijp); -   -  audioDevice->unregisterPort(port); -   -  return true; -} -*/ -  //---------------------------------------------------------  //   MidiJackDevice  //   in_jack_port or out_jack_port can be null  //--------------------------------------------------------- -//MidiJackDevice::MidiJackDevice(const int& a, const QString& n) -//MidiJackDevice::MidiJackDevice(jack_port_t* jack_port, const QString& n) -// p3.3.55 -//MidiJackDevice::MidiJackDevice(jack_port_t* in_jack_port, jack_port_t* out_jack_port, const QString& n)  MidiJackDevice::MidiJackDevice(const QString& n)     : MidiDevice(n)  { -  //_client_jackport = 0; -   -  //_client_jackport = jack_port; -  // p3.3.55 -  //_in_client_jackport  = in_jack_port; -  //_out_client_jackport = out_jack_port;    _in_client_jackport  = NULL;    _out_client_jackport = NULL; -   -  //adr = a;    init();  } @@ -179,10 +68,6 @@ MidiJackDevice::~MidiJackDevice()      printf("MidiJackDevice::~MidiJackDevice()\n");    #endif   -  //if(_client_jackport) -  //  audioDevice->unregisterPort(_client_jackport); -  // p3.3.55 -      if(audioDevice)    {       if(_in_client_jackport) @@ -194,69 +79,13 @@ MidiJackDevice::~MidiJackDevice()      //close();  } -/* -//--------------------------------------------------------- -//   select[RW]fd -//--------------------------------------------------------- - -int MidiJackDevice::selectRfd() -{ -  return jackmidi_pi[0]; -} - -int MidiJackDevice::selectWfd() -{ -  return jackmidi_po[0]; -} -*/ -  //---------------------------------------------------------  //   createJackMidiDevice  //   If name parameter is blank, creates a new (locally) unique one.  //--------------------------------------------------------- -//QString MidiJackDevice::createJackMidiDevice(int rwflags) // 1:Writable 2: Readable. Do not mix. -//MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable. Do not mix. -MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // p3.3.55 1:Writable 2: Readable 3: Writable + Readable +MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable 3: Writable + Readable  { -///  _openFlags &= _rwFlags; // restrict to available bits -   -///  #ifdef JACK_MIDI_DEBUG -///  printf("MidiJackDevice::open %s\n", name.toLatin1().constData()); -///  #endif   -   -  //jack_port_t* jp = jack_port_by_name(_client, name().toLatin1().constData()); -///  jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().toLatin1().constData()); -   -///  if(!jp) -///  { -///    printf("MidiJackDevice::open: Jack midi port %s not found!\n", name().toLatin1().constData()); -///    _writeEnable = false; -///    _readEnable = false; -///    return QString("Jack midi port not found"); -///  } -     -///  int pf = jack_port_flags(jp); -   -  //if(!name.isEmpty()) -  //{ -  //  Does not work. -  //  if(audioDevice->findPort(name.toLatin1().constData())) -  //  { -  //    fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Given port name %s already exists!\n", name.toLatin1().constData()); -  //    return 0;   -  //  }   -  //} -   -  //jack_port_t* client_jackport = NULL; -  // p3.3.55 -  ///jack_port_t* in_client_jackport = NULL; -  ///jack_port_t* out_client_jackport = NULL; -   -  //char buf[80]; -     -     -  // p3.3.55    int ni = 0;    if(name.isEmpty())    { @@ -273,188 +102,7 @@ MidiDevice* MidiJackDevice::createJackMidiDevice(QString name, int rwflags) // p      return 0;    } -  // If Jack port can receive data from us and we actually want to... -  //if((pf & JackPortIsInput) && (_openFlags & 1)) -  ///if(rwflags & 1) -  ///{ -    /*  p3.3.55 Removed. -    if(name.isEmpty()) -    { -      //snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); -      for(int i = 0; ; ++i) -      { -        //snprintf(buf, 80, "midi-out-%d", i); -        name.sprintf("midi-out-%d", i); -         -        if(!midiDevices.find(name)) -        { -          // Does not work. -          //if(!audioDevice->findPort(buf)) -          //  break; -          //client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); -          if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO)   // p3.3.52 -          { -            //client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.toLatin1().constData(), true); -            out_client_jackport = (jack_port_t*)audioDevice->registerOutPort((name + QString("_out")).toLatin1().constData(), true);  // p3.3.55 -            //if(client_jackport) -            if(out_client_jackport)  // p3.3.55 -              break; -          } -          else -            break;     -        }     -           -        if(i == 65535) -        { -          fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused output port name!\n"); -          return 0; -        } -      } -      //name = QString(buf); -    } -    else -    */ -     -    /* -    { -      if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO)       // p3.3.52 -      { -        //client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.toLatin1().constData(), true); -        out_client_jackport = (jack_port_t*)audioDevice->registerOutPort((name + QString(JACK_MIDI_OUT_PORT_SUFFIX)).toLatin1().constData(), true);  // p3.3.55 -        //if(!client_jackport) -        if(!out_client_jackport)   // p3.3.55 -        { -          //fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed creating output port name %s\n", name.toLatin1().constData()); -          fprintf(stderr, "MusE: createJackMidiDevice failed creating output port name %s\n", (name + QString(JACK_MIDI_OUT_PORT_SUFFIX)).toLatin1().constData()); // p3.3.55 -           -          //return 0; -          rwflags &= ~1; // p3.3.55 Remove the output r/w flag, but continue on... -        } -      }   -    } -    */ -     -    /* -    else -    { -      client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.toLatin1().constData(), true); -      if(!client_jackport) -      { -        for(int i = 0; ; ++i) -        { -          snprintf(buf, 80, "midi-out-%d", i); -          // Does not work! -          //if(!audioDevice->findPort(buf)) -          //  break; -          client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); -          if(client_jackport) -            break; -             -          if(i == 65535) -          { -            fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused output port name!\n"); -            return 0; -          } -        } -        name = QString(buf); -      }     -    } -    */ -     -    //client_jackport = (jack_port_t*)audioDevice->registerOutPort(name.toLatin1().constData(), true); -    //if(client_jackport == NULL) -    //{ -    //  fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed to register jack midi client output port %s\n", name.toLatin1().constData()); -    //  return 0; -    //} -    //else -    //  _nextOutIdNum++; -     -  ///} -  //else // Note docs say it can't be both input and output.  // p3.3.55 Removed -   -  // If Jack port can send data to us and we actually want it... -  //if((pf & JackPortIsOutput) && (_openFlags & 2)) -  ///if(rwflags & 2) -  ///{   -    /*  p3.3.55 Removed. -    if(name.isEmpty()) -    { -      //snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); -      for(int i = 0; ; ++i) -      { -        //snprintf(buf, 80, "midi-in-%d", i); -        name.sprintf("midi-in-%d", i);  -         -        if(!midiDevices.find(name)) -        { -          // Does not work. -          //if(!audioDevice->findPort(buf)) -          //  break; -          //client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); -          if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO)       // p3.3.52 -          { -            //client_jackport = (jack_port_t*)audioDevice->registerInPort(name.toLatin1().constData(), true); -            in_client_jackport = (jack_port_t*)audioDevice->registerInPort(name.toLatin1().constData(), true);  // p3.3.55 -            //if(client_jackport) -            if(in_client_jackport)   // p3.3.55 -              break; -          } -          else -            break;     -        }     -           -        if(i == 65535) -        { -          fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed! Can't find unused input port name!\n"); -          return 0; -        } -      } -      //name = QString(buf); -    } -    else -    */ -     -    /* -    { -      if(audioDevice->deviceType() == AudioDevice::JACK_AUDIO)       // p3.3.52 -      { -        //client_jackport = (jack_port_t*)audioDevice->registerInPort(name.toLatin1().constData(), true); -        in_client_jackport = (jack_port_t*)audioDevice->registerInPort((name + QString(JACK_MIDI_IN_PORT_SUFFIX)).toLatin1().constData(), true);   // p3.3.55 -        //if(!client_jackport) -        if(!in_client_jackport)    // p3.3.55 -        { -          //fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed creating input port name %s\n", name.toLatin1().constData()); -          fprintf(stderr, "MusE: createJackMidiDevice failed creating input port name %s\n", (name + QString(JACK_MIDI_IN_PORT_SUFFIX)).toLatin1().constData()); -           -          //return 0; -          rwflags &= ~2; // p3.3.55 Remove the input r/w flag, but continue on... -        }   -      } -    } -    */   -       -    //client_jackport = (jack_port_t*)audioDevice->registerInPort(name.toLatin1().constData(), true); -     -    //if(client_jackport == NULL) -    //{ -    //  fprintf(stderr, "MidiJackDevice::createJackMidiDevice failed to register jack midi client input port %s\n", name.toLatin1().constData()); -      //_readEnable = false; -      //return QString("Could not register jack-midi-in client port"); -    //  return 0; -    //} -    //else -    //  _nextInIdNum++; -     -  ///} -     -  //if(client_jackport == NULL)  // p3.3.52 Removed. Allow the device to be created even if Jack isn't running. -  //  return 0; -     -  //MidiJackDevice* dev = new MidiJackDevice(client_jackport, name); -  //MidiJackDevice* dev = new MidiJackDevice(in_client_jackport, out_client_jackport, name);  // p3.3.55 -  //MidiJackDevice* dev = new MidiJackDevice(NULL, NULL, name);  // p3.3.55 -  MidiJackDevice* dev = new MidiJackDevice(name);  // p3.3.55 +  MidiJackDevice* dev = new MidiJackDevice(name);      dev->setrwFlags(rwflags);    midiDevices.add(dev);    return dev; @@ -471,9 +119,6 @@ void MidiJackDevice::setName(const QString& s)    #endif      _name = s;  -  //if(clientPort())  // p3.3.52 Added check. -  //  audioDevice->setPortName(clientPort(), s.toLatin1().constData()); -  // p3.3.55    if(inClientPort())        audioDevice->setPortName(inClientPort(), (s + QString(JACK_MIDI_IN_PORT_SUFFIX)).toLatin1().constData());    if(outClientPort())   @@ -492,65 +137,7 @@ QString MidiJackDevice::open()    printf("MidiJackDevice::open %s\n", name().toLatin1().constData());    #endif   -  /* -  //jack_port_t* jp = jack_port_by_name(_client, name().toLatin1().constData()); -  jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().toLatin1().constData()); -   -  if(!jp) -  { -    printf("MidiJackDevice::open: Jack midi port %s not found!\n", name().toLatin1().constData()); -    _writeEnable = false; -    _readEnable = false; -    return QString("Jack midi port not found"); -  } -     -  int pf = jack_port_flags(jp); -   -  // If Jack port can receive data from us and we actually want to... -  if((pf & JackPortIsInput) && (_openFlags & 1)) -  { -    char buf[80]; -    snprintf(buf, 80, "muse-jack-midi-out-%d", _nextOutIdNum); -    _client_jackport = (jack_port_t*)audioDevice->registerOutPort(buf, true); -    if(_client_jackport == NULL) -    { -      fprintf(stderr, "MidiJackDevice::open failed to register jack-midi-out\n"); -      _writeEnable = false; -      return QString("Could not register jack-midi-out client port"); -    } -    else -    { -      _nextOutIdNum++; -      // src, dest -      ///audioDevice->connect(_client_jackport, jp); -      _writeEnable = true; -    } -  } -  else // Note docs say it can't be both input and output. -  // If Jack port can send data to us and we actually want it... -  if((pf & JackPortIsOutput) && (_openFlags & 2)) -  {   -    char buf[80]; -    snprintf(buf, 80, "muse-jack-midi-in-%d", _nextInIdNum); -    _client_jackport = (jack_port_t*)audioDevice->registerInPort(buf, true); -    if(_client_jackport == NULL) -    { -      fprintf(stderr, "MidiJackDevice::open failed to register jack-midi-in\n"); -      _readEnable = false; -      return QString("Could not register jack-midi-in client port"); -    } -    else -    { -      _nextInIdNum++; -      ///audioDevice->connect(jp, _client_jackport); -      _readEnable = true; -    } -  } -  */ -   -      QString s; -  // p3.3.55 Moved from createJackMidiDevice()    if(_openFlags & 1)    {      if(!_out_client_jackport) @@ -623,9 +210,6 @@ QString MidiJackDevice::open()      _in_client_jackport = NULL;      } -  //if(client_jackport == NULL)  // p3.3.52 Removed. Allow the device to be created even if Jack isn't running. -  //  return 0; -        _writeEnable = bool(_openFlags & 1);    _readEnable = bool(_openFlags & 2); @@ -642,10 +226,10 @@ void MidiJackDevice::close()    printf("MidiJackDevice::close %s\n", name().toLatin1().constData());    #endif   -  // p3.3.55 TODO: I don't really want to unregister the +  // TODO: I don't really want to unregister the    //  Jack midi ports because then we lose the connections    //  to Jack every time we click the read/write lights -  //  or change a port's device. +  //  or change a port's device.   p3.3.55     /*    if(_client_jackport) @@ -667,38 +251,6 @@ void MidiJackDevice::close()    _writeEnable = false;    _readEnable = false; -   -  /* -  //jack_port_t* jp = jack_port_by_name(_client, name().toLatin1().constData()); -  jack_port_t* jp = (jack_port_t*)audioDevice->findPort(name().toLatin1().constData()); -   -  if(!jp) -  { -    printf("MidiJackDevice::close: Jack midi port %s not found!\n", name().toLatin1().constData()); -    _writeEnable = false; -    _readEnable = false; -    return; -  } -     -  //int pf = jack_port_flags(jp); -   -  // If Jack port can receive data from us and we actually want to... -  //if((pf & JackPortIsInput) && (_openFlags & 1)) -  if(jack_port_connected_to(midi_port_out[0], name().toLatin1().constData())) -  { -    // src, dest -///    audioDevice->disconnect(midi_port_out[0], jp); -    _writeEnable = false; -  } -  else // Note docs say it can't be both input and output.   -  // If Jack port can send data to us and we actually want it... -  //if((pf & JackPortIsOutput) && (_openFlags & 2)) -  if(jack_port_connected_to(midi_port_in[0], name().toLatin1().constData())) -  {   -///    audioDevice->disconnect(jp, midi_port_in[0]); -    _readEnable = false; -  } -  */  }  //--------------------------------------------------------- @@ -707,7 +259,6 @@ void MidiJackDevice::close()  void MidiJackDevice::writeRouting(int level, Xml& xml) const  { -      // p3.3.45        // If this device is not actually in use by the song, do not write any routes.        // This prevents bogus routes from being saved and propagated in the med file.        if(midiPort() == -1) @@ -716,30 +267,17 @@ void MidiJackDevice::writeRouting(int level, Xml& xml) const        QString s;        if(rwFlags() & 2)  // Readable        { -        //RouteList* rl = _inRoutes; -        //for (ciRoute r = rl->begin(); r != rl->end(); ++r)           for (ciRoute r = _inRoutes.begin(); r != _inRoutes.end(); ++r)           {            if(!r->name().isEmpty())            {              xml.tag(level++, "Route"); -             -            //xml.strTag(level, "srcNode", r->name()); -            //xml.tag(level, "source type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData());              s = QT_TRANSLATE_NOOP("@default", "source");              if(r->type != Route::TRACK_ROUTE)                s += QString(QT_TRANSLATE_NOOP("@default", " type=\"%1\"")).arg(r->type); - -            //s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(r->name());              s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(Xml::xmlString(r->name()));              xml.tag(level, s.toLatin1().constData()); -             -            //xml.strTag(level, "dstNode", name()); -            //xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().toLatin1().constData()); -            //xml.tag(level, "dest type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().toLatin1().constData()); -            //xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, name().toLatin1().constData());              xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).toLatin1().constData()); -                          xml.etag(level--, "Route");            }          }   @@ -752,49 +290,14 @@ void MidiJackDevice::writeRouting(int level, Xml& xml) const            s = QT_TRANSLATE_NOOP("@default", "Route");            if(r->channel != -1)              s += QString(QT_TRANSLATE_NOOP("@default", " channel=\"%1\"")).arg(r->channel); -           -          //xml.tag(level++, "Route");            xml.tag(level++, s.toLatin1().constData()); -           -          /* -          //xml.strTag(level, "srcNode", name()); -          if(r->channel != -1)   -            //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, r->channel, name().toLatin1().constData()); -            //xml.tag(level, "source type=\"%d\" channel=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, r->channel, name().toLatin1().constData()); -            xml.tag(level, "source devtype=\"%d\" channel=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, r->channel, name().toLatin1().constData()); -          else   -            //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().toLatin1().constData()); -            //xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::MIDI_DEVICE_ROUTE, name().toLatin1().constData()); -          */   -            //xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, name().toLatin1().constData()); -            xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).toLatin1().constData()); -           -          /* -          //xml.strTag(level, "dstNode", r->name()); -          if(r->channel != -1) -          {   -            if(r->type == Route::MIDI_DEVICE_ROUTE) -              xml.tag(level, "dest devtype=\"%d\" channel=\"%d\" name=\"%s\"/", r->device->deviceType(), r->channel, r->name().toLatin1().constData()); -            else   -              xml.tag(level, "dest type=\"%d\" channel=\"%d\" name=\"%s\"/", r->type, r->channel, r->name().toLatin1().constData()); -          } -          else   -          { -            if(r->type == Route::MIDI_DEVICE_ROUTE) -              xml.tag(level, "dest devtype=\"%d\" name=\"%s\"/", r->device->deviceType(), r->name().toLatin1().constData()); -            else   -              xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); -          } -          */ -           +          xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::JACK_MIDI, Xml::xmlString(name()).toLatin1().constData());            s = QT_TRANSLATE_NOOP("@default", "dest");            if(r->type == Route::MIDI_DEVICE_ROUTE)              s += QString(QT_TRANSLATE_NOOP("@default", " devtype=\"%1\"")).arg(r->device->deviceType());            else            if(r->type != Route::TRACK_ROUTE)              s += QString(QT_TRANSLATE_NOOP("@default", " type=\"%1\"")).arg(r->type); - -          //s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(r->name());            s += QString(QT_TRANSLATE_NOOP("@default", " name=\"%1\"/")).arg(Xml::xmlString(r->name()));            xml.tag(level, s.toLatin1().constData()); @@ -802,33 +305,6 @@ void MidiJackDevice::writeRouting(int level, Xml& xml) const            xml.etag(level--, "Route");          }        } -       -      /* -      else -      if(rwFlags() & 1)  // Writable -      { -        //RouteList* rl = _outRoutes; -        //for (ciRoute r = rl->begin(); r != rl->end(); ++r)  -        for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r)  -        { -          if(!r->name().isEmpty()) -          { -            xml.tag(level++, "Route"); -             -            //xml.strTag(level, "srcNode", name()); -            //if(r->channel != -1)   -            //  xml.tag(level, "srcNode type=\"%d\" channel=\"%d\" name=\"%s\"", Route::JACK_MIDI_ROUTE, r->channel, name().toLatin1().constData()); -            //else   -              xml.tag(level, "source type=\"%d\" name=\"%s\"/", Route::JACK_MIDI_ROUTE, name().toLatin1().constData()); -             -            //xml.strTag(level, "dstNode", r->name()); -            xml.tag(level, "dest type=\"%d\" name=\"%s\"/", r->type, r->name().toLatin1().constData()); -             -            xml.etag(level--, "Route"); -          } -        }   -      } -      */        }  //--------------------------------------------------------- @@ -841,80 +317,8 @@ void MidiJackDevice::writeRouting(int level, Xml& xml) const   */  bool MidiJackDevice::putMidiEvent(const MidiPlayEvent& /*event*/)  { -  /* -  int give, channel = event.channel(); -  int x; - -  if(channel >= JACK_MIDI_CHANNELS) return false; - -  // buffer up events, because jack eats them in chunks, if -   // the buffer is full, there isn't so much to do, than -   // drop the event -    -  give = jack_midi_out_data[channel].give; -  if(jack_midi_out_data[channel].buffer[give*4+3]){ -    fprintf(stderr, "WARNING: muse-to-jack midi-buffer is full, channel=%u\n", channel); -    return false; -  } -  // copy event(note-on etc..), pitch and volume  -  // see http://www.midi.org/techspecs/midimessages.php  -  switch(event.type()){ -    case ME_NOTEOFF: -      jack_midi_out_data[channel].buffer[give*4+0] = 0x80; -      jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; -      jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f; -      break; -    case ME_NOTEON: -      jack_midi_out_data[channel].buffer[give*4+0] = 0x90; -      jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; -      jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f; -      break; -    case ME_CONTROLLER: -      jack_midi_out_data[channel].buffer[give*4+0] = 0xb0; -      jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; -      jack_midi_out_data[channel].buffer[give*4+2] = event.dataB() & 0x7f; -      break; -    case ME_PROGRAM: -      jack_midi_out_data[channel].buffer[give*4+0] = 0xc0; -      jack_midi_out_data[channel].buffer[give*4+1] = event.dataA() & 0x7f; -      jack_midi_out_data[channel].buffer[give*4+2] = 0; -      break; -    case ME_PITCHBEND: -      jack_midi_out_data[channel].buffer[give*4+0] = 0xE0; -      // convert muse pitch-bend to midi standard  -      x = 0x2000 + event.dataA(); -      jack_midi_out_data[channel].buffer[give*4+1] = x & 0x7f; -      jack_midi_out_data[channel].buffer[give*4+2] = (x >> 8) & 0x7f; -      break; -    default: -      fprintf(stderr, "jack-midi-out %u WARNING: unknown event %x\n", channel, event.type()); -      return false; -  } -  jack_midi_out_data[channel].buffer[give*4+3] = 1; // mark state of this slot  -  // finally increase give position  -  give++; -  if(give >= JACK_MIDI_BUFFER_SIZE){ -    give = 0; -  } -  jack_midi_out_data[channel].give = give; -  return false; -  */ -   -  return false; -} - -/* -//--------------------------------------------------------- -//   putEvent -//    return false if event is delivered -//--------------------------------------------------------- - -bool MidiJackDevice::putEvent(int* event) -{ -  int *y; y = event;    return false;  } -*/  //---------------------------------------------------------  //   recordEvent @@ -1004,9 +408,8 @@ void MidiJackDevice::recordEvent(MidiRecordEvent& event)        //if(_recordFifo.put(MidiPlayEvent(event)))        //  printf("MidiJackDevice::recordEvent: fifo overflow\n"); -      // p3.3.38        // Do not bother recording if it is NOT actually being used by a port. -      // Because from this point on, process handles things, by selected port. +      // Because from this point on, process handles things, by selected port.    p3.3.38        if(_port == -1)          return; @@ -1025,7 +428,7 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev)        MidiRecordEvent event;        event.setB(0); -      // NOTE: From MusE-2. Not done here in Muse-1 (yet). +      // NOTE: From muse_qt4_evolution. Not done here in Muse-2 (yet).        // move all events 2*MusEGlobal::segmentSize into the future to get        // jitterfree playback        // @@ -1034,15 +437,11 @@ void MidiJackDevice::eventReceived(jack_midi_event_t* ev)        //               ^          ^          ^        //               catch      process    play        // -//      const SeqTime* st = audio->seqTime(); - -      //unsigned curFrame = st->startFrame() + MusEGlobal::segmentSize; -//      unsigned curFrame = st->lastFrameTime; +              //int frameOffset = audio->getFrameOffset();        unsigned pos = audio->pos().frame(); -      //event.setTime(pos + ev->time); -      event.setTime(extSyncFlag.value() ? lastExtMidiSyncTick : (pos + ev->time)); +      event.setTime(extSyncFlag.value() ? lastExtMidiSyncTick : (pos + ev->time));      // p3.3.25        event.setChannel(*(ev->buffer) & 0xf);        int type = *(ev->buffer) & 0xf0; @@ -1144,13 +543,10 @@ void MidiJackDevice::collectMidiEvents()    if(!_readEnable)      return; -  //if(!_client_jackport) -  if(!_in_client_jackport)  // p3.3.55 +  if(!_in_client_jackport)        return; -  //void* port_buf = jack_port_get_buffer(_client_jackport, MusEGlobal::segmentSize); -  void* port_buf = jack_port_get_buffer(_in_client_jackport, MusEGlobal::segmentSize);   // p3.3.55 -   +  void* port_buf = jack_port_get_buffer(_in_client_jackport, MusEGlobal::segmentSize);       jack_midi_event_t event;    jack_nframes_t eventCount = jack_midi_get_event_count(port_buf);    for (jack_nframes_t i = 0; i < eventCount; ++i)  @@ -1172,9 +568,7 @@ void MidiJackDevice::collectMidiEvents()  bool MidiJackDevice::putEvent(const MidiPlayEvent& ev)  { -  //if(!_writeEnable) -  if(!_writeEnable || !_out_client_jackport)  // p4.0.15 -    //return true; +  if(!_writeEnable || !_out_client_jackport)        return false;    #ifdef JACK_MIDI_DEBUG @@ -1193,9 +587,7 @@ bool MidiJackDevice::putEvent(const MidiPlayEvent& ev)  //   return true if successful  //--------------------------------------------------------- -//void JackAudioDevice::putEvent(Port port, const MidiEvent& e)  bool MidiJackDevice::queueEvent(const MidiPlayEvent& e) -//bool MidiJackDevice::queueEvent(const MidiPlayEvent& e)  {        // Perhaps we can find use for this value later, together with the Jack midi MusE port(s).        // No big deal if not. Not used for now. @@ -1212,11 +604,9 @@ bool MidiJackDevice::queueEvent(const MidiPlayEvent& e)        //if(MusEGlobal::debugMsg)        //  printf("MidiJackDevice::queueEvent\n"); -      //if(!_client_jackport)   -      if(!_out_client_jackport)   // p3.3.55 +      if(!_out_client_jackport)             return false; -      //void* pb = jack_port_get_buffer(_client_jackport, MusEGlobal::segmentSize); -      void* pb = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize);  // p3.3.55 +      void* pb = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize);          //unsigned frameCounter = ->frameTime();        int frameOffset = audio->getFrameOffset(); @@ -1340,7 +730,7 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event)    // Just do this 'standard midi 64T timing thing' for now until we figure out more precise external timings.     // Does require relatively short audio buffers, in order to catch the resolution, but buffer <= 256 should be OK...     // Tested OK so far with 128.  -  ///if(extSyncFlag.value())  +  //if(extSyncFlag.value())     // p4.0.15 Or, is the event marked to be played immediately?    // Nothing to do but stamp the event to be queued for frame 0+.    if(t == 0 || extSyncFlag.value())     @@ -1377,17 +767,13 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event)    if(event.type() == ME_PITCHBEND)     {        int v = a + 8192; -      // p3.3.44        //printf("MidiJackDevice::processEvent ME_PITCHBEND v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); -              if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)))          return false;    }    else    if(event.type() == ME_CONTROLLER)     { -    //int a      = event.dataA(); -    //int b      = event.dataB();      // Perhaps we can find use for this value later, together with the Jack midi MusE port(s).      // No big deal if not. Not used for now.      //int port   = event.port(); @@ -1407,9 +793,7 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event)      if(a == CTRL_PITCH)       {        int v = b + 8192; -      // p3.3.44        //printf("MidiJackDevice::processEvent CTRL_PITCH v:%d time:%d type:%d ch:%d A:%d B:%d\n", v, event.time(), event.type(), event.channel(), event.dataA(), event.dataB()); -              if(!queueEvent(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f)))          return false;      } @@ -1420,8 +804,6 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event)              int hb = (b >> 16) & 0xff;              int lb = (b >> 8) & 0xff;              int pr = b & 0x7f; -           -            // p3.3.44              //printf("MidiJackDevice::processEvent CTRL_PROGRAM time:%d type:%d ch:%d A:%d B:%d hb:%d lb:%d pr:%d\n",               //       event.time(), event.type(), event.channel(), event.dataA(), event.dataB(), hb, lb, pr); @@ -1440,7 +822,7 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event)        //      }      } -    else if (a == CTRL_MASTER_VOLUME) // Enabled p4.0.15 Tim. +    else if (a == CTRL_MASTER_VOLUME)       {        unsigned char sysex[] = {              0x7f, 0x7f, 0x04, 0x01, 0x00, 0x00 @@ -1596,19 +978,16 @@ bool MidiJackDevice::processEvent(const MidiPlayEvent& event)  }  //--------------------------------------------------------- -//    processMidi called from audio process only. +//    processMidi  +//    Called from audio thread only.  //---------------------------------------------------------  void MidiJackDevice::processMidi()  { -  //if(!_client_jackport) -  //if(!_out_client_jackport)  // p3.3.55 -  //  return; +  processStuckNotes();        -  //void* port_buf = jack_port_get_buffer(_client_jackport, MusEGlobal::segmentSize); -  //void* port_buf = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize);   // p3.3.55    void* port_buf = 0; -  if(_out_client_jackport && _writeEnable)  // p4.0.15 +  if(_out_client_jackport && _writeEnable)      {      port_buf = jack_port_get_buffer(_out_client_jackport, MusEGlobal::segmentSize);      jack_midi_clear_buffer(port_buf); @@ -1616,45 +995,24 @@ void MidiJackDevice::processMidi()    while(!eventFifo.isEmpty())    { -    ///MidiPlayEvent e(eventFifo.get()); -    MidiPlayEvent e(eventFifo.peek()); // p4.0.15 -     -    ///int evTime = e.time();  -    // Is event marked to be played immediately? p4.0.15 Moved into processEvent(). -    ///if(evTime == 0)  -    ///{ -      // Nothing to do but stamp the event to be queued for frame 0+. -      //e.setTime(frameOffset + pos); -    ///  e.setTime(audio->getFrameOffset() + audio->pos().frame()); -    ///} -     -    //#ifdef JACK_MIDI_DEBUG -    //printf("MidiJackDevice::processMidi eventFifo time:%d type:%d ch:%d A:%d B:%d\n", e.time(), e.type(), e.channel(), e.dataA(), e.dataB());   -    //#endif   -     -    //el->insert(eventFifo.get()); -    //el->insert(e); -    ///processEvent(e); -    // p4.0.15 Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. +    MidiPlayEvent e(eventFifo.peek());  +    // Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events.  p4.0.15       if(port_buf && !processEvent(e))          return;            // Give up. The Jack buffer is full. Nothing left to do.        eventFifo.remove();  // Successfully processed event. Remove it from FIFO.    } -  MPEventList* el = playEvents(); -  if(el->empty()) +  if(_playEvents.empty())    {      //printf("MidiJackDevice::processMidi play events empty\n");       return;    } -  //printf("MidiJackDevice::processMidi play events:\n");  -  ///iMPEvent i = nextPlayEvent(); -  iMPEvent i = el->begin();     // p4.0.15 -  for(; i != el->end(); ++i)  +  iMPEvent i = _playEvents.begin();      +  for(; i != _playEvents.end(); ++i)     {      //printf("MidiJackDevice::processMidi playEvent time:%d type:%d ch:%d A:%d B:%d\n", i->time(), i->type(), i->channel(), i->dataA(), i->dataB());  -    // p3.3.39 Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values. +    // Update hardware state so knobs and boxes are updated. Optimize to avoid re-setting existing values.         // Same code as in MidiPort::sendEvent()      if(_port != -1)      { @@ -1672,12 +1030,10 @@ void MidiJackDevice::processMidi()        if(i->type() == ME_PITCHBEND)         {          //printf("MidiJackDevice::processMidi playEvents ME_PITCHBEND time:%d type:%d ch:%d A:%d B:%d\n", (*i).time(), (*i).type(), (*i).channel(), (*i).dataA(), (*i).dataB()); -                  int da = mp->limitValToInstrCtlRange(CTRL_PITCH, i->dataA());          if(!mp->setHwCtrlState(i->channel(), CTRL_PITCH, da))            continue;          //mp->setHwCtrlState(i->channel(), CTRL_PITCH, da); -                  //(MidiPlayEvent(t, port, chn, ME_PITCHBEND, v & 0x7f, (v >> 7) & 0x7f));        }        else @@ -1689,25 +1045,11 @@ void MidiJackDevice::processMidi()        }      } -    ///processEvent(*i); -    // p4.0.15 Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events. -    if(port_buf && !processEvent(*i)) -    { -      //setNextPlayEvent(i); -      //return; +    // Try to process only until full, keep rest for next cycle. If no out client port or no write enable, eat up events.  p4.0.15  +    if(port_buf && !processEvent(*i))         break; -    }      } -   -  ///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); -  // setNextPlayEvent(el->begin());  // Removed p4.0.15 Tim. +  _playEvents.erase(_playEvents.begin(), i);  } @@ -1718,139 +1060,6 @@ void MidiJackDevice::processMidi()  bool initMidiJack()  { -  /* -  int adr = 0; - -  memset(jack_midi_out_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); -  memset(jack_midi_in_data, 0, JACK_MIDI_CHANNELS * sizeof(muse_jack_midi_buffer)); - -  MidiJackDevice* dev = new MidiJackDevice(adr, QString("jack-midi")); -  dev->setrwFlags(3); // set read and write flags  - -  if(pipe(jackmidi_pi) < 0){ -    fprintf(stderr, "cant create midi-jack input pipe\n"); -  } -  if(pipe(jackmidi_po) < 0){ -    fprintf(stderr, "cant create midi-jack output pipe\n"); -  } -   -  midiDevices.add(dev); -   -  gmdev = dev; // proclaim the global jack-midi instance  - -  //jackScanMidiPorts(); -  */ -      return false;  } -/* -struct JackPort { -      int adr; -      //char* name; -      QString name; -      int flags; -      //JackPort(int a, const char* s, int f) { -      JackPort(int a, const QString& s, int f) { -            adr = a; -            //name = strdup(s); -            name = QString(s); -            flags = f; -            } -      }; - - -static std::list<JackPort> portList; - -//--------------------------------------------------------- -//   jackScanMidiPorts -//--------------------------------------------------------- - -void jackScanMidiPorts() -{ -  int adr; -  const char* name; - -  portList.clear(); -  adr  = 0; -  name = strdup("namex"); -  portList.push_back(JackPort(adr, name, 0)); -  // -  //  check for devices to add -  // -  for (std::list<JackPort>::iterator k = portList.begin(); k != portList.end(); ++k) { -    iMidiDevice i = midiDevices.begin(); -    for (;i != midiDevices.end(); ++i) { -      //MidiJackDevice* d = dynamic_cast<MidiJackDevice*>(*i); -      break; -      //if (d == 0) continue; -      //if ((k->adr.client == d->adr.client) && (k->adr.port == d->adr.port)) { -      //  break; -      //} -    } -    if (i == midiDevices.end()) { -      // add device -      MidiJackDevice* dev = new MidiJackDevice(k->adr, QString(k->name)); -      dev->setrwFlags(k->flags); -      midiDevices.add(dev); -    } -  } -} -*/ - -/* -//--------------------------------------------------------- -//   processInput -//--------------------------------------------------------- -static void handle_jack_midi_in(int channel) -{ -  MidiRecordEvent event; -  int t,n,v; -  t = jack_midi_in_data[channel].buffer[0]; -  n = jack_midi_in_data[channel].buffer[1]; -  v = jack_midi_in_data[channel].buffer[2]; - -  event.setType(0);      // mark as unused -  event.setPort(gmdev->midiPort()); -  event.setB(0); - -  if(t == 0x90){ // note on  -    fprintf(stderr, "jackProcessMidiInput note-on\n"); -    event.setChannel(channel); -    event.setType(ME_NOTEON); -    event.setA(n); -    event.setB(v); -  }else if (t == 0x80){ // note off  -    fprintf(stderr, "jackProcessMidiInput note-off\n"); -    event.setChannel(channel); -    event.setType(ME_NOTEOFF); -    event.setA(n); -    event.setB(v); -  }else{ -    fprintf(stderr, "WARNING: unknown midi-in on channel %d: %x,%x,%x\n", -            channel, t, n, v); -    return; -  } -  if(event.type()){ -    gmdev->recordEvent(event); -    midiPorts[gmdev->midiPort()].syncInfo().trigActDetect(event.channel()); -  } -} - -void MidiJackDevice::processInput() -{ -  char buf; -  int i,s; -  read(gmdev->selectRfd(), &buf, 1); - -  s = 1; -  for(i = 0; i < JACK_MIDI_CHANNELS; i++){ -    if(jack_midi_in_data[i].buffer[3]){ -      s = 0; -      handle_jack_midi_in(i); -      jack_midi_in_data[i].buffer[3] = 0; -    } -  } -} - -*/ diff --git a/muse2/muse/driver/jackmidi.h b/muse2/muse/driver/jackmidi.h index 2c027c00..e79e9288 100644 --- a/muse2/muse/driver/jackmidi.h +++ b/muse2/muse/driver/jackmidi.h @@ -3,6 +3,7 @@  //  Linux Music Editor  //  $Id: jackmidi.h,v 1.1.1.1 2010/01/27 09:06:43 terminator356 Exp $  //  (C) Copyright 1999-2010 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -40,81 +41,17 @@ class MidiPlayEvent;  //class RouteList;  class Xml; -// Turn on to show multiple devices, work in progress,  -//  not working fully yet, can't seem to connect... -//#define JACK_MIDI_SHOW_MULTIPLE_DEVICES -  // It appears one client port per remote port will be necessary.  // Jack doesn't seem to like manipulation of non-local ports buffers.  //#define JACK_MIDI_USE_MULTIPLE_CLIENT_PORTS -/* jack-midi channels */ -//#define JACK_MIDI_CHANNELS 32 - -/* jack-midi buffer size */ -//#define JACK_MIDI_BUFFER_SIZE 32 - -/* -typedef struct { -  int  give; -  int  take; -  // 32 parallel midi events, where each event contains three -  //  midi-bytes and one busy-byte  -  char buffer[4 * JACK_MIDI_BUFFER_SIZE]; -} muse_jack_midi_buffer; -*/ - -/* -struct JackMidiPort  -{ -  jack_port_t* _jackPort; -  QString _name; -  int _flags; // 1 = writable, 2 = readable - do not mix -  JackMidiPort(jack_port_t* jp, const QString& s, int f)  -  { -    _jackPort = jp; -    _name = QString(s); -    _flags = f; -  } -}; - -typedef std::map<jack_port_t*, JackMidiPort, std::less<jack_port_t*> >::iterator iJackMidiPort; -typedef std::map<jack_port_t*, JackMidiPort, std::less<jack_port_t*> >::const_iterator ciJackMidiPort; - -class JackMidiPortList : public std::map<jack_port_t*, JackMidiPort, std::less<jack_port_t*> >  -{ -   private:    -      static int _nextOutIdNum; -      static int _nextInIdNum; -       -   public: -      JackMidiPortList(); -      ~JackMidiPortList(); -      iJackMidiPort createClientPort(int flags); -      bool removeClientPort(jack_port_t* port); -}; - -extern JackMidiPortList jackMidiClientPorts; -*/ -  //---------------------------------------------------------  //   MidiJackDevice  //---------------------------------------------------------  class MidiJackDevice : public MidiDevice { -   public: -      //int adr; -     private: -      // fifo for midi events sent from gui -      // direct to midi port: -      //MidiFifo eventFifo;  // Moved into MidiDevice p4.0.15 - -      //static int _nextOutIdNum; -      //static int _nextInIdNum; -      //jack_port_t* _client_jackport; -      // p3.3.55        jack_port_t* _in_client_jackport;        jack_port_t* _out_client_jackport; @@ -134,49 +71,27 @@ class MidiJackDevice : public MidiDevice {        void eventReceived(jack_midi_event_t*);     public: -      //MidiJackDevice() {}  // p3.3.55  Removed. -      //MidiJackDevice(const int&, const QString& name); -       -      //MidiJackDevice(jack_port_t* jack_port, const QString& name); -      //MidiJackDevice(jack_port_t* in_jack_port, jack_port_t* out_jack_port, const QString& name); // p3.3.55 In or out port can be null.        MidiJackDevice(const QString& name);  -      //static MidiDevice* createJackMidiDevice(QString /*name*/, int /*rwflags*/); // 1:Writable 2: Readable. Do not mix. -      static MidiDevice* createJackMidiDevice(QString name = "", int rwflags = 3); // p3.3.55 1:Writable 2: Readable 3: Writable + Readable -       +      static MidiDevice* createJackMidiDevice(QString name = "", int rwflags = 3); // 1:Writable 2: Readable 3: Writable + Readable        virtual inline int deviceType() const { return JACK_MIDI; }  -              virtual void setName(const QString&);        virtual void processMidi();        virtual ~MidiJackDevice();  -      //virtual int selectRfd(); -      //virtual int selectWfd(); -      //virtual void processInput();        virtual void recordEvent(MidiRecordEvent&);        virtual bool putEvent(const MidiPlayEvent&);        virtual void collectMidiEvents(); -      //virtual jack_port_t* jackPort() { return _jackport; } -      //virtual jack_port_t* clientJackPort() { return _client_jackport; } -       -      //virtual void* clientPort() { return (void*)_client_jackport; } -      // p3.3.55        virtual void* inClientPort()  { return (void*)  _in_client_jackport; }        virtual void* outClientPort() { return (void*) _out_client_jackport; } -      //RouteList* routes()   { return &_routes; } -      //bool noRoute() const   { return _routes.empty();  }        virtual void writeRouting(int, Xml&) const;        };  extern bool initMidiJack(); -//extern int jackSelectRfd(); -//extern int jackSelectWfd(); -//extern void jackProcessMidiInput(); -//extern void jackScanMidiPorts();  #endif diff --git a/muse2/muse/instruments/minstrument.cpp b/muse2/muse/instruments/minstrument.cpp index dfdf94c6..20bee129 100644 --- a/muse2/muse/instruments/minstrument.cpp +++ b/muse2/muse/instruments/minstrument.cpp @@ -559,6 +559,7 @@ void MidiInstrument::reset(int portNo, MType)                    // These loops send 2048 events, which is more than our FIFO (or Jack buffer) can handle!   p4.0.15 Tim.                    // Nope, instead, increased FIFO sizes to accommodate.                    //port->device()->playEvents()->add(ev); +                  //port->device()->addScheduledEvent(ev);              }        }  } diff --git a/muse2/muse/midi.cpp b/muse2/muse/midi.cpp index 42f5bed7..39642348 100644 --- a/muse2/muse/midi.cpp +++ b/muse2/muse/midi.cpp @@ -4,6 +4,7 @@  //  $Id: midi.cpp,v 1.43.2.22 2009/11/09 20:28:28 terminator356 Exp $  //  //  (C) Copyright 1999/2004 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -216,9 +217,7 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track,                 || ((ev.channel() == track->outChannel()) && (ev.port() == track->outPort()))))                    continue;              unsigned tick = ev.time(); -            // Added by Tim. p3.3.8 -            // Added by T356.              if(doLoops)              {                if(tick >= song->lPos().tick() && tick < song->rPos().tick()) @@ -475,13 +474,10 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track,        //    read NoteOn events and remove corresponding NoteOffs        //--------------------------------------------------- -      // Added by Tim. p3.3.8  -      // Loop removed by flo                for (iEvent i = mel.begin(); i != mel.end(); ++i) {                Event ev  = i->second;                if (ev.isNote()) {                      if (!ev.isNoteOff()) { -                    // Added by Tim. p3.3.8                      // If the event length is not zero, it means the event and its                       //  note on/off have already been taken care of. So ignore it. @@ -520,10 +516,10 @@ void buildMidiEventList(EventList* del, const MPEventList* el, MidiTrack* track,                            if (k==i)                               //this will never happen, because i->second has to be a NOTE ON,                              //while k has to be a NOTE OFF. but in case something changes: -														printf("ERROR: THIS SHOULD NEVER HAPPEN: k==i in midi.cpp:buildMidiEventList()\n"); +                            printf("ERROR: THIS SHOULD NEVER HAPPEN: k==i in midi.cpp:buildMidiEventList()\n");                            else -														mel.erase(k); -                           +                            mel.erase(k); +                            i = mel.begin();   // p4.0.34                            continue;                            }                      } @@ -690,8 +686,6 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts)        int defaultPort = port;        MidiDevice* md          = midiPorts[port].device(); -      MPEventList* playEvents = md->playEvents(); -      MPEventList* stuckNotes = md->stuckNotes();        PartList* pl = track->parts();        for (iPart p = pl->begin(); p != pl->end(); ++p) { @@ -714,7 +708,7 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts)                    continue;              unsigned stick = (offset > cts) ? 0 : cts - offset;              unsigned etick = nts - offset; -            // By T356. Do not play events which are past the end of this part.  +            // Do not play events which are past the end of this part.               if(etick > partLen)                continue; @@ -776,33 +770,26 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts)                                int veloOff = ev.veloOff();                                if (port == defaultPort) { -                                    //printf("Adding event normally: frame=%d port=%d channel=%d pitch=%d velo=%d\n",frame, port, channel, pitch, velo); -                                     -                                    // 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. +                                    // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. p3.3.25                                      if(extSyncFlag.value()) -                                      playEvents->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo)); +                                      md->addScheduledEvent(MidiPlayEvent(tick, port, channel, ME_NOTEON, pitch, velo));                                      else -                                     -                                      playEvents->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo)); +                                      md->addScheduledEvent(MidiPlayEvent(frame, port, channel, ME_NOTEON, pitch, velo)); -                                    stuckNotes->add(MidiPlayEvent(tick + len, port, channel, -                                       veloOff ? 0x80 : 0x90, pitch, veloOff)); +                                    md->addStuckNote(MidiPlayEvent(tick + len, port, channel, +                                       veloOff ? ME_NOTEOFF : ME_NOTEON, pitch, veloOff));                                         }                                else { //Handle events to different port than standard.                                      MidiDevice* mdAlt = midiPorts[port].device();                                      if (mdAlt) { -                                         -                                        // p3.3.25 -                                        if(extSyncFlag.value()) -                                          mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel, 0x90, pitch, velo)); +                                        if(extSyncFlag.value())  // p3.3.25 +                                          mdAlt->addScheduledEvent(MidiPlayEvent(tick, port, channel, ME_NOTEON, pitch, velo));                                                                                    else   +                                          mdAlt->addScheduledEvent(MidiPlayEvent(frame, port, channel, ME_NOTEON, pitch, velo));                                           -                                          mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel, 0x90, pitch, velo)); -                                           -                                        mdAlt->stuckNotes()->add(MidiPlayEvent(tick + len, port, channel, -                                          veloOff ? 0x80 : 0x90, pitch, veloOff)); +                                        mdAlt->addStuckNote(MidiPlayEvent(tick + len, port, channel, +                                          veloOff ? ME_NOTEOFF : ME_NOTEON, pitch, veloOff));                                        }                                      } @@ -811,7 +798,6 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts)                                }                                break; -                        // Added by T356.                          case Controller:                                {                                  if (track->type() == Track::DRUM)   @@ -829,37 +815,30 @@ void Audio::collectEvents(MidiTrack* track, unsigned int cts, unsigned int nts)                                      MidiDevice* mdAlt = midiPorts[port].device();                                      if(mdAlt)                                       { -                                      // 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. +                                      // Therefore we cannot get sub-tick resolution. Just use ticks instead of frames. p3.3.25                                        if(extSyncFlag.value()) -                                        mdAlt->playEvents()->add(MidiPlayEvent(tick, port, channel,  +                                        mdAlt->addScheduledEvent(MidiPlayEvent(tick, port, channel,                                                                                ME_CONTROLLER, ctl | pitch, ev.dataB()));                                        else -                                        mdAlt->playEvents()->add(MidiPlayEvent(frame, port, channel,  +                                        mdAlt->addScheduledEvent(MidiPlayEvent(frame, port, channel,                                                                                ME_CONTROLLER, ctl | pitch, ev.dataB())); -                                                                                                                   }                                        break;                                    }                                    } -                                // p3.3.25 -                                if(extSyncFlag.value()) -                                  playEvents->add(MidiPlayEvent(tick, port, channel, ev)); +                                if(extSyncFlag.value())  // p3.3.25 +                                  md->addScheduledEvent(MidiPlayEvent(tick, port, channel, ev));                                  else   -                                 -                                  playEvents->add(MidiPlayEvent(frame, port, channel, ev)); +                                  md->addScheduledEvent(MidiPlayEvent(frame, port, channel, ev));                                }                                     break; -                                    -                                    +                                                  default: -                              // p3.3.25 -                              if(extSyncFlag.value()) -                                playEvents->add(MidiPlayEvent(tick, port, channel, ev)); +                              if(extSyncFlag.value())  // p3.3.25 +                                md->addScheduledEvent(MidiPlayEvent(tick, port, channel, ev));                                else -                                 -                                playEvents->add(MidiPlayEvent(frame, port, channel, ev)); +                                md->addScheduledEvent(MidiPlayEvent(frame, port, channel, ev));                                break;                          } @@ -885,30 +864,6 @@ void Audio::processMidi()        for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) {              MidiDevice* md = *id; -            // -            // erase already played events: -            // -            ///MPEventList* playEvents = md->playEvents();      -            ///iMPEvent nextPlayEvent  = md->nextPlayEvent();   -             -            //if(md->playEvents()->size())        -            //{ -              //printf("Audio::processMidi before erase md play events size:%d nextPlayEvent isEnd:%d\n", md->playEvents()->size(), md->nextPlayEvent() == md->playEvents()->end());   -            //  printf("Audio::processMidi md play events size:%d first item:\n", md->playEvents()->size());   -            //  md->playEvents()->begin()->dump();   -            //}   -             -            // p4.0.15 Tim. Moved into MidiJackDevice::processMidi (for Jack), and MidiSeq::processTimerTick (for ALSA). -            // Why do this here? Instead, let each device erase their just-played events. -            ///playEvents->erase(playEvents->begin(), nextPlayEvent); -             -            //if(playEvents->size())   -            //{ -            //  printf("Audio::processMidi after erase md play events size:%d nextPlayEvent isEnd:%d events:\n", playEvents->size(), nextPlayEvent == playEvents->end());   -              //for(iMPEvent ie = playEvents->begin(); ie != playEvents->end(); ++ie) -              //  ie->dump(); -            //}   -              // klumsy hack for synti devices:              if(md->isSynti())              { @@ -920,10 +875,6 @@ void Audio::processMidi()                }              } -            // Is it a Jack midi device?  -            //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); -            //if(mjd) -            //  mjd->collectMidiEvents();              md->collectMidiEvents();              // Take snapshots of the current sizes of the recording fifos,  @@ -931,33 +882,14 @@ void Audio::processMidi()              md->beforeProcess();              } -      // p4.0.15 Tim. Moved into SynthI::getData. -      // Why do this here? Instead, let each device erase their just-played events. -      ///MPEventList* playEvents = metronome->playEvents(); -      ///iMPEvent nextPlayEvent  = metronome->nextPlayEvent(); -      ///playEvents->erase(playEvents->begin(), nextPlayEvent); - -      // p3.3.25        bool extsync = extSyncFlag.value(); -              for (iMidiTrack t = song->midis()->begin(); t != song->midis()->end(); ++t)         {              MidiTrack* track = *t;              int port = track->outPort();              MidiDevice* md = midiPorts[port].device(); -             -            // Changed by Tim. p3.3.8 -            //if(md == 0) -            //  continue; -            //MPEventList* playEvents = md->playEvents(); -            //if (playEvents == 0) -            //    continue; -            //if (!track->isMute())  -            MPEventList* playEvents = 0;              if(md)              { -              playEvents = md->playEvents(); -                              // only add track events if the track is unmuted                if(!track->isMute())                 { @@ -971,66 +903,26 @@ void Audio::processMidi()              //              if (track->recordFlag())               { -                  //int portMask    = track->inPortMask(); -                  // p3.3.38 Removed -                  //unsigned int portMask = track->inPortMask(); -                  //int channelMask = track->inChannelMask(); -                                      MPEventList* rl = track->mpevents();                    MidiPort* tport = &midiPorts[port]; - -                  // p3.3.38 -                  //for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id)  -                  //{                    RouteList* irl = track->inRoutes();                    for(ciRoute r = irl->begin(); r != irl->end(); ++r)                    { -                        //if(!r->isValid() || (r->type != Route::ALSA_MIDI_ROUTE && r->type != Route::JACK_MIDI_ROUTE)) -                        //if(!r->isValid() || (r->type != Route::MIDI_DEVICE_ROUTE))                          if(!r->isValid() || (r->type != Route::MIDI_PORT_ROUTE))   // p3.3.49                            continue; -                            -                        int devport = r->midiPort;     // p3.3.49 +                        int devport = r->midiPort;                                 //                           if (devport == -1) -                              continue; -                         -                        //MidiDevice* dev = *id; -                        //MidiDevice* dev = r->device; -                        MidiDevice* dev = midiPorts[devport].device();   // p3.3.49 +                          continue; +                        MidiDevice* dev = midiPorts[devport].device();             //                           if(!dev)                            continue; -                         -                         -                        // p3.3.50 Removed -                        //int channel = r->channel;     -                        // NOTE: TODO: Special for input device sysex 'channel' marked as -1, ** IF we end up going with that method **. -                        // This would mean having a separate 'System' channel listed in the routing popups. -                        // The other alternative is to accept sysex from a device as long as ANY regular channel is routed from it,  -                        //  this does not require a 'System' channel listed in the routing popups. -                        // But that requires more code below... Done. -                        //if(channel == -1) -                          //channel = MIDI_CHANNELS; // Special channel '17' -                        //  continue; -                           -                        //int devport = dev->midiPort(); -                   -                        // record only from ports marked in portMask: -                        //if (devport == -1 || !(portMask & (1 << devport))) -                        //if (devport == -1) -                        //      continue; -                               -                        //MREventList* el = dev->recordEvents(); -                        //MidiFifo& rf = dev->recordEvents(); -                         -                         -                        int channelMask = r->channel;   // p3.3.50 +                        int channelMask = r->channel;                             if(channelMask == -1 || channelMask == 0)                            continue; -                        for(int channel = 0; channel < MIDI_CHANNELS; ++channel)     // p3.3.50 +                        for(int channel = 0; channel < MIDI_CHANNELS; ++channel)                               {                            if(!(channelMask & (1 << channel)))                              continue; -                                                    if(!dev->sysexFIFOProcessed())                            {                              // Set to the sysex fifo at first. @@ -1041,323 +933,192 @@ void Audio::processMidi()                              for(int i = 0; i < count; ++i)                               {                                MidiPlayEvent event(rf.peek(i)); -                               -                              //unsigned time = event.time() + segmentSize*(segmentCount-1); -                              //unsigned time = event.time() + (extsync ? MusEConfig::config.division/24 : segmentSize*(segmentCount-1)); -                              //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); -                              //event.setTime(time); -                              //if(!extsync) -                              //  event.setTime(event.time() + segmentSize*(segmentCount-1)); -                                  event.setPort(port); -                                                              // dont't echo controller changes back to software                                // synthesizer:                                if(!dev->isSynti() && md && track->recEcho()) -                                playEvents->add(event); -                               -                              // If syncing externally the event time is already in units of ticks, set above. +                                md->addScheduledEvent(event); +                              // If syncing externally the event time is already in units of ticks, set above.  p3.3.25                                if(!extsync) -                              { -                                //time = tempomap.frame2tick(event.time()); -                                //event.setTime(time);  // set tick time                                  event.setTime(tempomap.frame2tick(event.time()));  // set tick time -                              }   -                                  if(recording)                                   rl->add(event);                              }       -                                                          dev->setSysexFIFOProcessed(true);                            } -                          // Set to the sysex fifo at first. -                          ///MidiFifo& rf = dev->recordEvents(MIDI_CHANNELS); -                          // Get the frozen snapshot of the size. -                          ///int count = dev->tmpRecordCount(MIDI_CHANNELS); -                           -                          // Iterate once for sysex fifo (if needed), once for channel fifos. -                          ///for(int sei = 0; sei < 2; ++sei) +                          MidiRecFifo& rf = dev->recordEvents(channel); +                          int count = dev->tmpRecordCount(channel); +                          for(int i = 0; i < count; ++i)                             { -                            // If on first pass, do sysex fifo.   -                            /* -                            if(sei == 0) -                            {   -                              // Ignore any further channel routes on this device if already done here. -                              if(dev->sysexFIFOProcessed()) -                                continue; -                              // Go ahead and set this now. -                              dev->setSysexFIFOProcessed(true); -                              // Allow it to fall through with the sysex fifo and count... -                            } -                            else -                            { -                              // We're on the second pass, do channel fifos. -                              rf = dev->recordEvents(channel); -                              // Get the frozen snapshot of the size. -                              count = dev->tmpRecordCount(channel); -                            } -                            */ -                             -                            MidiRecFifo& rf = dev->recordEvents(channel); -                            int count = dev->tmpRecordCount(channel); -                             -                            //for (iMREvent ie = el->begin(); ie != el->end(); ++ie)  -                            for(int i = 0; i < count; ++i)  -                            { -                                  MidiPlayEvent event(rf.peek(i)); -                                   -                                  //int channel = ie->channel(); -                                  ///int channel = event.channel(); -                                   -                                  int defaultPort = devport; -                                  ///if (!(channelMask & (1 << channel))) -                                  ///{ -                                  ///      continue; -                                  ///}       -     -                                  //MidiPlayEvent event(*ie); -                                  int drumRecPitch=0; //prevent compiler warning: variable used without initialization -                                  MidiController *mc = 0; -                                  int ctl = 0; -                                   -                                  //Hmmm, hehhh...  -                                  // TODO: Clean up a bit around here when it comes to separate events for rec & for playback.  -                                  // But not before 0.7 (ml) -     -                                  int prePitch = 0, preVelo = 0; -     -                                  event.setChannel(track->outChannel()); -                                   -                                  if (event.isNote() || event.isNoteOff())  +                                MidiPlayEvent event(rf.peek(i)); +                                int defaultPort = devport; +                                int drumRecPitch=0; //prevent compiler warning: variable used without initialization +                                MidiController *mc = 0; +                                int ctl = 0; +                                 +                                //Hmmm, hehhh...  +                                // TODO: Clean up a bit around here when it comes to separate events for rec & for playback.  +                                // But not before 0.7 (ml) +   +                                int prePitch = 0, preVelo = 0; +   +                                event.setChannel(track->outChannel()); +                                 +                                if (event.isNote() || event.isNoteOff())  +                                { +                                      // +                                      // apply track values +                                      // +   +                                      //Apply drum inkey: +                                      if (track->type() == Track::DRUM)  +                                      { +                                            int pitch = event.dataA(); +                                            //Map note that is played according to drumInmap +                                            drumRecPitch = drumMap[(unsigned int)drumInmap[pitch]].enote; +                                            devport = drumMap[(unsigned int)drumInmap[pitch]].port; +                                            event.setPort(devport); +                                            channel = drumMap[(unsigned int)drumInmap[pitch]].channel; +                                            event.setA(drumMap[(unsigned int)drumInmap[pitch]].anote); +                                            event.setChannel(channel); +                                      } +                                      else  +                                      { //Track transpose if non-drum +                                            prePitch = event.dataA(); +                                            int pitch = prePitch + track->transposition; +                                            if (pitch > 127) +                                                  pitch = 127; +                                            if (pitch < 0) +                                                  pitch = 0; +                                            event.setA(pitch); +                                      } +   +                                      if (!event.isNoteOff())  +                                      { +                                            preVelo = event.dataB(); +                                            int velo = preVelo + track->velocity; +                                            velo = (velo * track->compression) / 100; +                                            if (velo > 127) +                                                  velo = 127; +                                            if (velo < 1) +                                                  velo = 1; +                                            event.setB(velo); +                                      } +                                } +                                else +                                if(event.type() == ME_CONTROLLER) +                                { +                                  if(track->type() == Track::DRUM)                                     { -                                        // -                                        // apply track values -                                        // -     -                                        //Apply drum inkey: -                                        if (track->type() == Track::DRUM)  -                                        { -                                              int pitch = event.dataA(); -                                              //Map note that is played according to drumInmap -                                              drumRecPitch = drumMap[(unsigned int)drumInmap[pitch]].enote; -                                              devport = drumMap[(unsigned int)drumInmap[pitch]].port; -                                              event.setPort(devport); -                                              channel = drumMap[(unsigned int)drumInmap[pitch]].channel; -                                              event.setA(drumMap[(unsigned int)drumInmap[pitch]].anote); -                                              event.setChannel(channel); -                                        } -                                        else  -                                        { //Track transpose if non-drum -                                              prePitch = event.dataA(); -                                              int pitch = prePitch + track->transposition; -                                              if (pitch > 127) -                                                    pitch = 127; -                                              if (pitch < 0) -                                                    pitch = 0; -                                              event.setA(pitch); +                                    ctl = event.dataA(); +                                    // Regardless of what port the event came from, is it a drum controller event  +                                    //  according to the track port's instrument? +                                    mc = tport->drumController(ctl); +                                    if(mc) +                                    { +                                      int pitch = ctl & 0x7f; +                                      ctl &= ~0xff; +                                      int dmindex = drumInmap[pitch] & 0x7f; +                                      //Map note that is played according to drumInmap +                                      drumRecPitch = drumMap[dmindex].enote; +                                      devport = drumMap[dmindex].port; +                                      event.setPort(devport); +                                      channel = drumMap[dmindex].channel; +                                      event.setA(ctl | drumMap[dmindex].anote); +                                      event.setChannel(channel); +                                    }   +                                  } +                                } +                                 +                                // MusE uses a fixed clocks per quarternote of 24.  +                                // At standard 384 ticks per quarternote for example,  +                                // 384/24=16 for a division of 16 sub-frames (16 MusE 'ticks'). +                                // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). p3.3.35 +                                // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. +   +                                // dont't echo controller changes back to software +                                // synthesizer: +   +                                if (!dev->isSynti())  +                                { +                                  //Check if we're outputting to another port than default: +                                  if (devport == defaultPort) { +                                        event.setPort(port); +                                        if(md && track->recEcho()) +                                          //playEvents->add(event); +                                          md->addScheduledEvent(event);                                          } -     -                                        if (!event.isNoteOff())  -                                        { -                                              preVelo = event.dataB(); -                                              int velo = preVelo + track->velocity; -                                              velo = (velo * track->compression) / 100; -                                              if (velo > 127) -                                                    velo = 127; -                                              if (velo < 1) -                                                    velo = 1; -                                              event.setB(velo); +                                  else { +                                        // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? +                                        MidiDevice* mdAlt = midiPorts[devport].device(); +                                        if(mdAlt && track->recEcho()) +                                          //mdAlt->playEvents()->add(event); +                                          mdAlt->addScheduledEvent(event);                                          } -                                  } -                                  // Added by T356. -                                  else -                                  if(event.type() == ME_CONTROLLER) -                                  { -                                    if(track->type() == Track::DRUM)  -                                    { -                                      ctl = event.dataA(); -                                      // Regardless of what port the event came from, is it a drum controller event  -                                      //  according to the track port's instrument? -                                      mc = tport->drumController(ctl); -                                      if(mc) +                                  // Shall we activate meters even while rec echo is off? Sure, why not... +                                  if(event.isNote() && event.dataB() > track->activity()) +                                    track->setActivity(event.dataB()); +                                } +                                 +                                // If syncing externally the event time is already in units of ticks, set above.  p3.3.25 +                                if(!extsync) +                                  event.setTime(tempomap.frame2tick(event.time()));  // set tick time +   +                                // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml) +                                if (recording)  +                                { +                                      // In these next steps, it is essential to set the recorded event's port  +                                      //  to the track port so buildMidiEventList will accept it. Even though  +                                      //  the port may have no device "<none>". +                                      // +                                      if (track->type() == Track::DRUM)                                         { -                                        int pitch = ctl & 0x7f; -                                        ctl &= ~0xff; -                                        int dmindex = drumInmap[pitch] & 0x7f; -                                        //Map note that is played according to drumInmap -                                        drumRecPitch = drumMap[dmindex].enote; -                                        devport = drumMap[dmindex].port; -                                        event.setPort(devport); -                                        channel = drumMap[dmindex].channel; -                                        event.setA(ctl | drumMap[dmindex].anote); -                                        event.setChannel(channel); -                                      }   -                                    } -                                  } -                                   -                                  // p3.3.25  -                                  // MusE uses a fixed clocks per quarternote of 24.  -                                  // At standard 384 ticks per quarternote for example,  -                                  // 384/24=16 for a division of 16 sub-frames (16 MusE 'ticks'). -                                  // That is what we'll use if syncing externally. -                                  //unsigned time = event.time() + segmentSize*(segmentCount-1); -                                  //unsigned time = event.time() + (extsync ? MusEConfig::config.division/24 : segmentSize*(segmentCount-1)); -                                  // p3.3.34 -                                  // Oops, use the current tick.  -                                  //unsigned time = extsync ? curTickPos : (event.time() + segmentSize*(segmentCount-1)); -                                  //event.setTime(time); -                                  // p3.3.35 -                                  // If ext sync, events are now time-stamped with last tick in MidiDevice::recordEvent(). -                                  // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice. -                                  // p3.3.36 -                                  //if(!extsync) -                                  //  event.setTime(event.time() + segmentSize*(segmentCount-1)); -     -                                  // dont't echo controller changes back to software -                                  // synthesizer: -     -                                  if (!dev->isSynti())  -                                  { -                                    //Check if we're outputting to another port than default: -                                    if (devport == defaultPort) { -                                          event.setPort(port); -                                          if(md && track->recEcho()) -                                            playEvents->add(event); -                                          } -                                    else { -                                          // Hmm, this appears to work, but... Will this induce trouble with md->setNextPlayEvent?? -                                          MidiDevice* mdAlt = midiPorts[devport].device(); -                                          if(mdAlt && track->recEcho()) -                                            mdAlt->playEvents()->add(event); -                                          } -                                    // Shall we activate meters even while rec echo is off? Sure, why not... -                                    if(event.isNote() && event.dataB() > track->activity()) -                                      track->setActivity(event.dataB()); -                                  } -                                   -                                  // p3.3.25 -                                  // If syncing externally the event time is already in units of ticks, set above. -                                  if(!extsync) -                                  { -                                    // p3.3.35 -                                    //time = tempomap.frame2tick(event.time()); -                                    //event.setTime(time);  // set tick time -                                    event.setTime(tempomap.frame2tick(event.time()));  // set tick time -                                  }   -     -                                  // Special handling of events stored in rec-lists. a bit hACKish. TODO: Clean up (after 0.7)! :-/ (ml) -                                  if (recording)  -                                  { -                                        // In these next steps, it is essential to set the recorded event's port  -                                        //  to the track port so buildMidiEventList will accept it. Even though  -                                        //  the port may have no device "<none>". -                                        // -                                        if (track->type() == Track::DRUM)  -                                        { -                                          // Is it a drum controller event? -                                          if(mc) -                                          {     -                                              MidiPlayEvent drumRecEvent = event; -                                              drumRecEvent.setA(ctl | drumRecPitch); -                                              // In this case, preVelo is simply the controller value. -                                              drumRecEvent.setB(preVelo); -                                              drumRecEvent.setPort(port); //rec-event to current port -                                              drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel -                                              rl->add(drumRecEvent); -                                          } -                                          else -                                          { -                                           -                                              MidiPlayEvent drumRecEvent = event; -                                              drumRecEvent.setA(drumRecPitch); -                                              drumRecEvent.setB(preVelo); -                                              // Changed by T356.  -                                              // Tested: Events were not being recorded for a drum map entry pointing to a  -                                              //  different port. This must have been wrong - buildMidiEventList would ignore this. -                                              //drumRecEvent.setPort(devport); -                                              drumRecEvent.setPort(port);  //rec-event to current port -                                               -                                              drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel -                                              rl->add(drumRecEvent); -                                          }     +                                        // Is it a drum controller event? +                                        if(mc) +                                        {     +                                            MidiPlayEvent drumRecEvent = event; +                                            drumRecEvent.setA(ctl | drumRecPitch); +                                            // In this case, preVelo is simply the controller value. +                                            drumRecEvent.setB(preVelo); +                                            drumRecEvent.setPort(port); //rec-event to current port +                                            drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel +                                            rl->add(drumRecEvent);                                          } -                                        else  +                                        else                                          { -                                              // Restore record-pitch to non-transposed value since we don't want the note transposed twice next -                                              MidiPlayEvent recEvent = event; -                                              if (prePitch) -                                                    recEvent.setA(prePitch); -                                              if (preVelo) -                                                    recEvent.setB(preVelo); -                                              recEvent.setPort(port); -                                              recEvent.setChannel(track->outChannel()); -                                                     -                                              rl->add(recEvent); -                                        } -                                  } +                                            MidiPlayEvent drumRecEvent = event; +                                            drumRecEvent.setA(drumRecPitch); +                                            drumRecEvent.setB(preVelo); +                                            // Tested: Events were not being recorded for a drum map entry pointing to a  +                                            //  different port. This must have been wrong - buildMidiEventList would ignore this. Tim. +                                            //drumRecEvent.setPort(devport); +                                            drumRecEvent.setPort(port);  //rec-event to current port +                                             +                                            drumRecEvent.setChannel(track->outChannel()); //rec-event to current channel +                                            rl->add(drumRecEvent); +                                        }     +                                      } +                                      else  +                                      { +                                            // Restore record-pitch to non-transposed value since we don't want the note transposed twice next +                                            MidiPlayEvent recEvent = event; +                                            if (prePitch) +                                                  recEvent.setA(prePitch); +                                            if (preVelo) +                                                  recEvent.setB(preVelo); +                                            recEvent.setPort(port); +                                            recEvent.setChannel(track->outChannel()); +                                                   +                                            rl->add(recEvent); +                                      } +                                }                              } -                          }                            }                    }              } -            // Added by Tim. p3.3.8 -            // Removed p4.0.15 -            ///if(md) -            ///  md->setNextPlayEvent(playEvents->begin()); -            //if(md)   -            //{ -            //  if(md->nextPlayEvent() != playEvents->begin()) -            //    printf("Audio::processMidi md->nextPlayEvent() != playEvents->begin()\n");   -            //}          } -      // -      // clear all recorded events in midiDevices -      // process stuck notes -      // -      for (iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id) { -            MidiDevice* md = *id; -             -            ///md->recordEvents()->clear(); -            // By T356. Done processing this rec buffer, now flip to the other one. -            ///md->flipRecBuffer(); -            // We are done with the 'frozen' recording fifos, remove the events.  -            md->afterProcess(); -             -            MPEventList* stuckNotes = md->stuckNotes(); -            MPEventList* playEvents = md->playEvents(); - -            iMPEvent k; -            for (k = stuckNotes->begin(); k != stuckNotes->end(); ++k) { -                  if (k->time() >= nextTickPos) -                        break; -                  MidiPlayEvent ev(*k); -                   -                  // p3.3.25 -                  //int frame = tempomap.tick2frame(k->time()) + frameOffset; -                  if(extsync) -                  { -                    ev.setTime(k->time()); -                  } -                  else -                  { -                    int frame = tempomap.tick2frame(k->time()) + frameOffset; -                    ev.setTime(frame); -                  }   -                   -                  // p3.3.25 -                  //ev.setTime(frame); -                   -                  playEvents->add(ev); -                  } -            stuckNotes->erase(stuckNotes->begin(), k); -            // Removed p4.0.15 Tim. -            ///md->setNextPlayEvent(playEvents->begin()); -            //if(md->nextPlayEvent() != playEvents->begin())    -            //  printf("Audio::processMidi clear notes: md->nextPlayEvent() != playEvents->begin()\n");   -            } -        //---------------------------------------------------        //    insert metronome clicks        //--------------------------------------------------- @@ -1366,35 +1127,21 @@ void Audio::processMidi()        if (MusEGlobal::midiClickFlag)              md = midiPorts[MusEGlobal::clickPort].device();        if (song->click() && (isPlaying() || state == PRECOUNT)) { -            MPEventList* playEvents = 0; -            MPEventList* stuckNotes = 0; -            if (md) { -                  playEvents = md->playEvents(); -                  stuckNotes = md->stuckNotes(); -                  }              int bar, beat;              unsigned tick;              bool isMeasure = false;              while (midiClick < nextTickPos) {                    if (isPlaying()) { -                        ///sigmap.tickValues(midiClick, &bar, &beat, &tick);                          AL::sigmap.tickValues(midiClick, &bar, &beat, &tick);                          isMeasure = beat == 0;                          }                    else if (state == PRECOUNT) {                          isMeasure = (clickno % clicksMeasure) == 0;                          } -                  // p3.3.25                    //int frame = tempomap.tick2frame(midiClick) + frameOffset; -                  int evtime = extsync ? midiClick : tempomap.tick2frame(midiClick) + frameOffset; +                  int evtime = extsync ? midiClick : tempomap.tick2frame(midiClick) + frameOffset;  // p3.3.25 -                  // p3.3.25 -                  //MidiPlayEvent ev(frame, MusEGlobal::clickPort, MusEGlobal::clickChan, ME_NOTEON, -                  MidiPlayEvent ev(evtime, MusEGlobal::clickPort, MusEGlobal::clickChan, ME_NOTEON, -                     MusEGlobal::beatClickNote, MusEGlobal::beatClickVelo); -                                         if (md) { -                        // p3.3.25                          //MidiPlayEvent ev(frame, MusEGlobal::clickPort, MusEGlobal::clickChan, ME_NOTEON,                          MidiPlayEvent ev(evtime, MusEGlobal::clickPort, MusEGlobal::clickChan, ME_NOTEON,                             MusEGlobal::beatClickNote, MusEGlobal::beatClickVelo); @@ -1403,32 +1150,20 @@ void Audio::processMidi()                                ev.setA(MusEGlobal::measureClickNote);                                ev.setB(MusEGlobal::measureClickVelo);                                } -                        playEvents->add(ev); -                        } -                  if (MusEGlobal::audioClickFlag) { -                        // p3.3.25 -                        //MidiPlayEvent ev1(frame, 0, 0, ME_NOTEON, 0, 0); -                        MidiPlayEvent ev1(evtime, 0, 0, ME_NOTEON, 0, 0); +                        md->addScheduledEvent(ev); -                        ev1.setA(isMeasure ? 0 : 1); -                        metronome->playEvents()->add(ev1); -                        } -                  if (md) {                          ev.setB(0); -                        // p3.3.25 -                        // Removed. Why was this here? -                        //frame = tempomap.tick2frame(midiClick+20) + frameOffset; -                        // -                        // Does it mean this should be changed too?  -                        // No, stuck notes are in units of ticks, not frames like (normal, non-external) play events...                          ev.setTime(midiClick+10); -                         -                        if (md) -                              stuckNotes->add(ev); +                        md->addStuckNote(ev); +                        } +                  if (MusEGlobal::audioClickFlag) { +                        //MidiPlayEvent ev1(frame, 0, 0, ME_NOTEON, 0, 0); +                        MidiPlayEvent ev(evtime, 0, 0, ME_NOTEON, 0, 0); +                        ev.setA(isMeasure ? 0 : 1); +                        metronome->addScheduledEvent(ev); +                        // Built-in metronome synth does not use stuck notes...                          } -                    if (isPlaying()) -                        ///midiClick = sigmap.bar2tick(bar, beat+1, 0);                          midiClick = AL::sigmap.bar2tick(bar, beat+1, 0);                    else if (state == PRECOUNT) {                          midiClick += ticksBeat; @@ -1438,94 +1173,21 @@ void Audio::processMidi()                                state = START_PLAY;                          }                    } -            // Removed p4.0.15 Tim. -            ///if (md) -            ///      md->setNextPlayEvent(playEvents->begin()); -            //if(md)   -            //{ -            //  if(md->nextPlayEvent() != playEvents->begin())    -            //    printf("Audio::processMidi metronome: md->nextPlayEvent() != playEvents->begin()\n");   -            //}     -             -            // Removed p4.0.15 Tim. -            ///if (MusEGlobal::audioClickFlag) -            ///      metronome->setNextPlayEvent(metronome->playEvents()->begin()); -            //if(MusEGlobal::audioClickFlag)   -            //{ -            //  if(metronome->nextPlayEvent() != metronome->playEvents()->begin())    -            //    printf("Audio::processMidi metronome: metronome->nextPlayEvent() != metronome->playEvents->begin()\n");   -            //}     -             -            } - -      if (state == STOP) { -            //--------------------------------------------------- -            //    end all notes -            //--------------------------------------------------- - -            for (iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) { -                  MidiDevice* md = *imd; -                  MPEventList* playEvents = md->playEvents(); -                  MPEventList* stuckNotes = md->stuckNotes(); -                  for (iMPEvent k = stuckNotes->begin(); k != stuckNotes->end(); ++k) { -                        MidiPlayEvent ev(*k); -                        ev.setTime(0);    // play now -                        playEvents->add(ev); -                        } -                  stuckNotes->clear(); -                  }              } -             -             -      // p3.3.36 -      //int tickpos = audio->tickPos(); -      //bool extsync = extSyncFlag.value(); +              // -      // Special for Jack midi devices: Play all Jack midi events up to curFrame. +      // Play all midi events up to curFrame.        //        for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id)         { -        //MidiDevice* md = *id; -        // Is it a Jack midi device? -        //MidiJackDevice* mjd = dynamic_cast<MidiJackDevice*>(md); -        //if(!mjd) -        //  continue; -        //mjd->processMidi(); -        (*id)->processMidi(); +        // We are done with the 'frozen' recording fifos, remove the events.  +        (*id)->afterProcess();   // 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(); -        for(; i != el->end(); ++i)  -        { -          // 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); -        */ +        // ALSA devices handled by another thread. +        if((*id)->deviceType() != MidiDevice::ALSA_MIDI) +        //if((*id)->deviceType() == MidiDevice::JACK_MIDI) +          (*id)->processMidi();        } -                    midiBusy=false;        } diff --git a/muse2/muse/midi.h b/muse2/muse/midi.h index 2bfe695b..f137d7e5 100644 --- a/muse2/muse/midi.h +++ b/muse2/muse/midi.h @@ -4,6 +4,7 @@  //  $Id: midi.h,v 1.4.2.2 2009/11/09 20:28:28 terminator356 Exp $  //  //  (C) Copyright 1999/2000 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -86,7 +87,6 @@ QString midiMetaName(int);  class EventList;  class MPEventList;  class MidiTrack; -//extern void buildMidiEventList(EventList* mel, const MPEventList* el, MidiTrack* track, int division, bool);  extern void buildMidiEventList(EventList* mel, const MPEventList* el, MidiTrack* track, int division, bool /*addSysexMeta*/, bool /*doLoops*/);  // extern bool checkSysex(MidiTrack* track, unsigned int len, unsigned char* buf); diff --git a/muse2/muse/mididev.cpp b/muse2/muse/mididev.cpp index a97ac1fb..a06303e1 100644 --- a/muse2/muse/mididev.cpp +++ b/muse2/muse/mididev.cpp @@ -4,6 +4,7 @@  //  $Id: mididev.cpp,v 1.10.2.6 2009/11/05 03:14:35 terminator356 Exp $  //  //  (C) Copyright 1999-2004 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -596,6 +597,34 @@ bool MidiDevice::putEvent(const MidiPlayEvent& ev)        }  //--------------------------------------------------------- +//   processStuckNotes +//--------------------------------------------------------- + +void MidiDevice::processStuckNotes()  +{ +  // Must be playing for valid nextTickPos, right? But wasn't checked in Audio::processMidi(). +  // audio->isPlaying() might not be true during seek right now. +  //if(audio->isPlaying())   +  { +    bool extsync = extSyncFlag.value(); +    int frameOffset = audio->getFrameOffset(); +    unsigned nextTick = audio->nextTick(); +    iMPEvent k; +    for (k = _stuckNotes.begin(); k != _stuckNotes.end(); ++k) { +          if (k->time() >= nextTick)   +                break; +          MidiPlayEvent ev(*k); +          if(extsync)              // p3.3.25 +            ev.setTime(k->time()); +          else  +            ev.setTime(tempomap.tick2frame(k->time()) + frameOffset); +          _playEvents.add(ev); +          } +    _stuckNotes.erase(_stuckNotes.begin(), k); +  }   +} + +//---------------------------------------------------------  //   handleStop  //--------------------------------------------------------- diff --git a/muse2/muse/mididev.h b/muse2/muse/mididev.h index 00e4ab65..cdb69ea2 100644 --- a/muse2/muse/mididev.h +++ b/muse2/muse/mididev.h @@ -4,6 +4,7 @@  //  $Id: mididev.h,v 1.3.2.4 2009/04/04 01:49:50 terminator356 Exp $  //  //  (C) Copyright 2000 Werner Schweer (ws@seh.de) +//  (C) Copyright 2011 Tim E. Real (terminator356 on users dot sourceforge dot net)  //  //  This program is free software; you can redistribute it and/or  //  modify it under the terms of the GNU General Public License @@ -41,8 +42,8 @@ class Xml;  //---------------------------------------------------------  class MidiDevice { -      MPEventList _stuckNotes; -      MPEventList _playEvents; +      //MPEventList _stuckNotes; +      //MPEventList _playEvents;        // Removed p4.0.15 Tim.        //iMPEvent _nextPlayEvent; @@ -71,6 +72,9 @@ class MidiDevice {        //bool _sysexWritingChunks;        bool _sysexReadingChunks; +      MPEventList _stuckNotes; +      MPEventList _playEvents; +              // Fifo for midi events sent from gui direct to midi port:        MidiFifo eventFifo;  // p4.0.15 @@ -84,6 +88,7 @@ class MidiDevice {        void init();        virtual bool putMidiEvent(const MidiPlayEvent&) = 0; +      virtual void processStuckNotes();     public:        enum { ALSA_MIDI=0, JACK_MIDI=1, SYNTH_MIDI=2 }; @@ -130,21 +135,24 @@ class MidiDevice {        virtual void recordEvent(MidiRecordEvent&); +      // Schedule an event for playback. Returns false if event cannot be delivered. +      virtual bool addScheduledEvent(const MidiPlayEvent& ev) { _playEvents.add(ev); return true; } +      // Add a stuck note. Returns false if event cannot be delivered. +      virtual bool addStuckNote(const MidiPlayEvent& ev) { _stuckNotes.add(ev); return true; } +      // Put an event for immediate playback.        virtual bool putEvent(const MidiPlayEvent&);        // This method will try to putEvent 'tries' times, waiting 'delayUs' microseconds between tries.        // Since it waits, it should not be used in RT or other time-sensitive threads. p4.0.15        bool putEventWithRetry(const MidiPlayEvent&, int /*tries*/ = 2, long /*delayUs*/ = 50000);  // 2 tries, 50 mS by default. -      // p4.0.22 -      virtual void handleStop(); +      virtual void handleStop();  // p4.0.22        virtual void handleSeek(); -      // For Jack-based devices - called in Jack audio process callback        virtual void collectMidiEvents() {}           virtual void processMidi() {} -      MPEventList* stuckNotes()          { return &_stuckNotes; } -      MPEventList* playEvents()          { return &_playEvents; } +      //MPEventList* stuckNotes()          { return &_stuckNotes; } +      //MPEventList* playEvents()          { return &_playEvents; }        ///MREventList* recordEvents();        ///void flipRecBuffer()               { _recBufFlipped = _recBufFlipped ? false : true; } diff --git a/muse2/muse/midiseq.cpp b/muse2/muse/midiseq.cpp index c2f3c7bd..2f1174cd 100644 --- a/muse2/muse/midiseq.cpp +++ b/muse2/muse/midiseq.cpp @@ -74,12 +74,15 @@ void MidiSeq::processMsg(const ThreadMsg* m)              //case MS_PROCESS:              //      audio->processMidi();              //      break; -            case SEQM_SEEK: -                  processSeek(); -                  break; -            case MS_STOP: -                  processStop(); -                  break; +             +            // Removed p4.0.34 +            //case SEQM_SEEK: +            //      processSeek(); +            //      break; +            //case MS_STOP: +            //      processStop(); +            //      break; +                          case MS_SET_RTC:                    MusEGlobal::doSetuid();                    setRtcTicks(); @@ -88,6 +91,8 @@ void MidiSeq::processMsg(const ThreadMsg* m)              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(); @@ -110,6 +115,7 @@ void MidiSeq::processMsg(const ThreadMsg* m)                    //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); @@ -141,6 +147,8 @@ void MidiSeq::processMsg(const ThreadMsg* m)              }        } +#if 0    +// Removed p4.0.34  //---------------------------------------------------------  //   processStop  //--------------------------------------------------------- @@ -148,8 +156,8 @@ void MidiSeq::processMsg(const ThreadMsg* m)  void MidiSeq::processStop()  {    // p3.3.28 -  // TODO Try to move this into Audio::stopRolling(). p4.0.22 -  playStateExt = false; // not playing +  // 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 @@ -157,10 +165,10 @@ void MidiSeq::processStop()    //    for(iMidiDevice id = midiDevices.begin(); id != midiDevices.end(); ++id)     { -    MidiDevice* md = *id; -    if(md->deviceType() == MidiDevice::JACK_MIDI)      // p4.0.22 -      continue;                                         -    md->handleStop();  // p4.0.22 +    //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; @@ -178,18 +186,20 @@ void MidiSeq::processStop()      */    }  } +#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 -  if (pos == 0 && !song->record()) -        audio->initDevices(); +  //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 @@ -197,13 +207,10 @@ void MidiSeq::processSeek()    for (iMidiDevice i = midiDevices.begin(); i != midiDevices.end(); ++i)     { -    MidiDevice* md = *i; -    // -    //    Jack midi devices are handled in Audio::seek() -    // -    if(md->deviceType() == MidiDevice::JACK_MIDI)      // p4.0.22 -      continue; -    md->handleSeek();  // p4.0.22 +    //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) @@ -254,6 +261,7 @@ void MidiSeq::processSeek()      */    }  } +#endif  //---------------------------------------------------------  //   MidiSeq @@ -427,7 +435,7 @@ void MidiSeq::updatePollFd()        for (iMidiDevice imd = midiDevices.begin(); imd != midiDevices.end(); ++imd) {              MidiDevice* dev = *imd;              int port = dev->midiPort(); -            const QString name = dev->name(); +            //const QString name = dev->name();              if (port == -1)                    continue;              if ((dev->rwFlags() & 0x2) || (extSyncFlag.value() @@ -725,20 +733,26 @@ void MidiSeq::processTimerTick()  //            }        // p3.3.25 -      int tickpos = audio->tickPos(); -      bool extsync = extSyncFlag.value(); +      //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; +            //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; +            //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(); @@ -776,6 +790,8 @@ void MidiSeq::processTimerTick()              // 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 +            */ +                          }        } @@ -813,8 +829,8 @@ void MidiSeq::msgSetMidiDevice(MidiPort* port, MidiDevice* device)  // 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); } -void MidiSeq::msgStop()         { msgMsg(MS_STOP); } +//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); } diff --git a/muse2/muse/midiseq.h b/muse2/muse/midiseq.h index 0cf85114..1e6ebf2a 100644 --- a/muse2/muse/midiseq.h +++ b/muse2/muse/midiseq.h @@ -91,6 +91,8 @@ class MidiSeq : public Thread {        virtual void threadStop();        virtual void threadStart(void*); +      bool externalPlayState() const { return playStateExt; } +      void setExternalPlayState(bool v) { playStateExt = v; }        void realtimeSystemInput(int, int);        void mtcInputQuarter(int, unsigned char);        void setSongPosition(int, int); @@ -101,9 +103,9 @@ class MidiSeq : public Thread {        void nonRealtimeSystemSysex(int, const unsigned char*, int);        void msgMsg(int id); -      void msgProcess(); -      void msgSeek(); -      void msgStop(); +      //void msgProcess(); +      //void msgSeek(); +      //void msgStop();        void msgSetRtc();        void msgUpdatePollFd();        void msgAddSynthI(SynthI* synth); diff --git a/muse2/muse/mpevent.h b/muse2/muse/mpevent.h index 6e9a1f90..4d957adf 100644 --- a/muse2/muse/mpevent.h +++ b/muse2/muse/mpevent.h @@ -154,8 +154,8 @@ class MidiPlayEvent : public MEvent {  typedef std::multiset<MidiPlayEvent, std::less<MidiPlayEvent>, audioRTalloc<MidiPlayEvent> > MPEL;  struct MPEventList : public MPEL { -      //void add(const MidiPlayEvent& ev) { MPEL::insert(ev); } -      iterator add(const MidiPlayEvent& ev) { return MPEL::insert(ev); }  // p4.0.15 We need the iterator. +      void add(const MidiPlayEvent& ev) { MPEL::insert(ev); } +      //iterator add(const MidiPlayEvent& ev) { return MPEL::insert(ev); }  // p4.0.15 We need the iterator.  };  typedef MPEventList::iterator iMPEvent; diff --git a/muse2/muse/song.cpp b/muse2/muse/song.cpp index 65bfec13..bec5aabc 100644 --- a/muse2/muse/song.cpp +++ b/muse2/muse/song.cpp @@ -1982,6 +1982,28 @@ void Song::processMsg(AudioMsg* msg)                    updateFlags = SC_EVENT_MODIFIED;                    break; +            // Moved here from MidiSeq::processMsg   p4.0.34 +            case SEQM_ADD_TRACK: +                  insertTrack2(msg->track, msg->ival); +                  break; +            case SEQM_REMOVE_TRACK: +                  //removeTrack2(msg->track); +                  cmdRemoveTrack(msg->track); +                  break; +            case SEQM_CHANGE_TRACK: +                  changeTrack((Track*)(msg->p1), (Track*)(msg->p2)); +                  break; +            case SEQM_ADD_PART: +                  cmdAddPart((Part*)msg->p1); +                  break; +            case SEQM_REMOVE_PART: +                  cmdRemovePart((Part*)msg->p1); +                  break; +            case SEQM_CHANGE_PART: +                  //cmdChangePart((Part*)msg->p1, (Part*)msg->p2); +                  cmdChangePart((Part*)msg->p1, (Part*)msg->p2, msg->a, msg->b); +                  break; +                          case SEQM_ADD_TEMPO:                    //printf("processMsg (SEQM_ADD_TEMPO) UndoOp::AddTempo. adding tempo at: %d with tempo=%d\n", msg->a, msg->b);                    addUndo(UndoOp(UndoOp::AddTempo, msg->a, msg->b)); diff --git a/muse2/muse/synth.cpp b/muse2/muse/synth.cpp index 5435f8ce..ae54cd15 100644 --- a/muse2/muse/synth.cpp +++ b/muse2/muse/synth.cpp @@ -323,6 +323,15 @@ bool SynthI::putEvent(const MidiPlayEvent& ev)  }  //--------------------------------------------------------- +//   processMidi +//--------------------------------------------------------- + +void SynthI::processMidi()  +{ +    processStuckNotes();  +} + +//---------------------------------------------------------  //   setName  //--------------------------------------------------------- @@ -962,12 +971,13 @@ void SynthI::preProcessAlways()    if(off())    {      // Clear any accumulated play events. -    playEvents()->clear(); +    //playEvents()->clear(); +    _playEvents.clear();      // Eat up any fifo events.      //while(!eventFifo.isEmpty())       //  eventFifo.get();   -    eventFifo.clear();  // p4.0.21 Duh, clear is the same but faster AND safer, right? -  }     +    eventFifo.clear();  // Clear is the same but faster AND safer, right? +  }  }  void MessSynthIF::preProcessAlways() @@ -987,12 +997,15 @@ bool SynthI::getData(unsigned pos, int ports, unsigned n, float** buffer)        int p = midiPort();        MidiPort* mp = (p != -1) ? &midiPorts[p] : 0; -      MPEventList* el = playEvents(); +      //MPEventList* el = playEvents();        ///iMPEvent ie = nextPlayEvent(); -      iMPEvent ie = el->begin();      // p4.0.15 Tim. +      //iMPEvent ie = el->begin();      // p4.0.15 Tim. +      iMPEvent ie = _playEvents.begin();       +       -      ie = _sif->getData(mp, el, ie, pos, ports, n, buffer); +      //ie = _sif->getData(mp, el, ie, pos, ports, n, buffer); +      ie = _sif->getData(mp, &_playEvents, ie, pos, ports, n, buffer);        ///setNextPlayEvent(ie);        // p4.0.15 We are done with these events. Let us erase them here instead of Audio::processMidi. @@ -1001,7 +1014,8 @@ bool SynthI::getData(unsigned pos, int ports, unsigned n, float** buffer)        //  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(), ie); +      //el->erase(el->begin(), ie); +      _playEvents.erase(_playEvents.begin(), ie);        // setNextPlayEvent(el->begin());   // Removed p4.0.15        return true; diff --git a/muse2/muse/synth.h b/muse2/muse/synth.h index a76d798d..fc442027 100644 --- a/muse2/muse/synth.h +++ b/muse2/muse/synth.h @@ -292,6 +292,7 @@ class SynthI : public AudioTrack, public MidiDevice,              }        bool putEvent(const MidiPlayEvent& ev); +      virtual void processMidi();        MidiPlayEvent receiveEvent() { return _sif->receiveEvent(); }        int eventsPending() const    { return _sif->eventsPending(); }  | 
