Audacity  3.0.3
MIDIPlay.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 @file MIDIPlay.cpp
6 @brief Inject added MIDI playback capability into Audacity's audio engine
7 
8 Paul Licameli split from AudioIO.cpp and AudioIOBase.cpp
9 
10 *//*****************************************************************/
357 #include "MIDIPlay.h"
358 #include "AudioIO.h"
359 
360 #include "BasicUI.h"
361 #include "Prefs.h"
362 #include "portaudio.h"
363 #include <portmidi.h>
364 #include <porttime.h>
365 #include <thread>
366 
367 #define ROUND(x) (int) ((x)+0.5)
368 
369 class NoteTrack;
370 using NoteTrackConstArray = std::vector < std::shared_ptr< const NoteTrack > >;
371 
372 namespace {
373 
374 /*
375  Adapt and rename the implementation of PaUtil_GetTime from commit
376  c5d2c51bd6fe354d0ee1119ba932bfebd3ebfacc of portaudio
377  */
378 #if defined( __APPLE__ )
379 
380 #include <mach/mach_time.h>
381 
382 /* Scaler to convert the result of mach_absolute_time to seconds */
383 static double machSecondsConversionScaler_ = 0.0;
384 
385 /* Initialize it */
386 static struct InitializeTime { InitializeTime() {
387  mach_timebase_info_data_t info;
388  kern_return_t err = mach_timebase_info( &info );
389  if( err == 0 )
390  machSecondsConversionScaler_ = 1e-9 * (double) info.numer / (double) info.denom;
391 } } initializeTime;
392 
393 static PaTime util_GetTime( void )
394 {
395  return mach_absolute_time() * machSecondsConversionScaler_;
396 }
397 
398 #elif defined( __WXMSW__ )
399 
400 #include "profileapi.h"
401 #include "sysinfoapi.h"
402 #include "timeapi.h"
403 
404 static int usePerformanceCounter_;
405 static double secondsPerTick_;
406 
407 static struct InitializeTime { InitializeTime() {
408  LARGE_INTEGER ticksPerSecond;
409 
410  if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
411  {
412  usePerformanceCounter_ = 1;
413  secondsPerTick_ = 1.0 / (double)ticksPerSecond.QuadPart;
414  }
415  else
416  {
417  usePerformanceCounter_ = 0;
418  }
419 } } initializeTime;
420 
421 static double util_GetTime( void )
422 {
423  LARGE_INTEGER time;
424 
425  if( usePerformanceCounter_ )
426  {
427  /*
428  Note: QueryPerformanceCounter has a known issue where it can skip forward
429  by a few seconds (!) due to a hardware bug on some PCI-ISA bridge hardware.
430  This is documented here:
431  http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&
432 
433  The work-arounds are not very paletable and involve querying GetTickCount
434  at every time step.
435 
436  Using rdtsc is not a good option on multi-core systems.
437 
438  For now we just use QueryPerformanceCounter(). It's good, most of the time.
439  */
440  QueryPerformanceCounter( &time );
441  return time.QuadPart * secondsPerTick_;
442  }
443  else
444  {
445  #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
446  return GetTickCount64() * .001;
447  #else
448  return timeGetTime() * .001;
449  #endif
450  }
451 }
452 
453 #elif defined(HAVE_CLOCK_GETTIME)
454 
455 #include <time.h>
456 
457 static PaTime util_GetTime( void )
458 {
459  struct timespec tp;
460  clock_gettime(CLOCK_REALTIME, &tp);
461  return (PaTime)(tp.tv_sec + tp.tv_nsec * 1e-9);
462 }
463 
464 #else
465 
466 #include <sys/time.h>
467 
468 static PaTime util_GetTime( void )
469 {
470  struct timeval tv;
471  gettimeofday( &tv, NULL );
472  return (PaTime) tv.tv_usec * 1e-6 + tv.tv_sec;
473 }
474 
475 #endif
476 
477 enum {
478  // This is the least positive latency we can
479  // specify to Pm_OpenOutput, 1 ms, which prevents immediate
480  // scheduling of events:
482 };
483 
484 // return the system time as a double
485 static double streamStartTime = 0; // bias system time to small number
486 
487 static double SystemTime(bool usingAlsa)
488 {
489 #ifdef __WXGTK__
490  if (usingAlsa) {
491  struct timespec now;
492  // CLOCK_MONOTONIC_RAW is unaffected by NTP or adj-time
493  clock_gettime(CLOCK_MONOTONIC_RAW, &now);
494  //return now.tv_sec + now.tv_nsec * 0.000000001;
495  return (now.tv_sec + now.tv_nsec * 0.000000001) - streamStartTime;
496  }
497 #else
498  static_cast<void>(usingAlsa);//compiler food.
499 #endif
500 
501  return util_GetTime() - streamStartTime;
502 }
503 
504 bool MIDIPlay::mMidiStreamActive = false;
505 bool MIDIPlay::mMidiOutputComplete = true;
506 
508  [](const auto &playbackSchedule){
509  return std::make_unique<MIDIPlay>(playbackSchedule);
510  }
511 };
512 
513 MIDIPlay::MIDIPlay(const PlaybackSchedule &schedule)
514  : mPlaybackSchedule{ schedule }
515 {
516 #ifdef AUDIO_IO_GB_MIDI_WORKAROUND
517  // Pre-allocate with a likely sufficient size, exceeding probable number of
518  // channels
519  mPendingNotesOff.reserve(64);
520 #endif
521 
522  PmError pmErr = Pm_Initialize();
523 
524  if (pmErr != pmNoError) {
525  auto errStr =
526  XO("There was an error initializing the midi i/o layer.\n");
527  errStr += XO("You will not be able to play midi.\n\n");
528  wxString pmErrStr = LAT1CTOWX(Pm_GetErrorText(pmErr));
529  if (!pmErrStr.empty())
530  errStr += XO("Error: %s").Format( pmErrStr );
531  // XXX: we are in libaudacity, popping up dialogs not allowed! A
532  // long-term solution will probably involve exceptions
533  using namespace BasicUI;
535  errStr,
537  .Caption(XO("Error Initializing Midi"))
538  .ButtonStyle(Button::Ok)
539  .IconStyle(Icon::Error));
540 
541  // Same logic for PortMidi as described above for PortAudio
542  }
543 }
544 
546 {
547  Pm_Terminate();
548 }
549 
551  const PaStreamInfo* info, double, double rate)
552 {
553  mMidiPlaybackTracks.clear();
554  for (const auto &pTrack : tracks.otherPlayableTracks) {
555  pTrack->TypeSwitch( [&](const NoteTrack *pNoteTrack){
556  mMidiPlaybackTracks.push_back(
557  pNoteTrack->SharedPointer<const NoteTrack>());
558  } );
559  }
560 
561  streamStartTime = 0;
563 
564  mNumFrames = 0;
565  // we want this initial value to be way high. It should be
566  // sufficient to assume AudioTime is zero and therefore
567  // mSystemMinusAudioTime is SystemTime(), but we'll add 1000s
568  // for good measure. On the first callback, this should be
569  // reduced to SystemTime() - mT0, and note that mT0 is always
570  // positive.
573  mAudioOutLatency = 0.0; // set when stream is opened
574  mCallbackCount = 0;
576 
577  // We use audio latency to estimate how far ahead of DACS we are writing
578  if (info) {
579  // this is an initial guess, but for PA/Linux/ALSA it's wrong and will be
580  // updated with a better value:
581  mAudioOutLatency = info->outputLatency;
583  }
584 
585  // TODO: it may be that midi out will not work unless audio in or out is
586  // active -- this would be a bug and may require a change in the
587  // logic here.
588 
589  bool successMidi = true;
590 
591  if(!mMidiPlaybackTracks.empty()){
592  successMidi = StartPortMidiStream(rate);
593  }
594 
595  // On the other hand, if MIDI cannot be opened, we will not complain
596  // return successMidi;
597  return true;
598 }
599 
601 {
602  mMidiPlaybackTracks.clear();
603 }
604 
605 PmTimestamp MidiTime(void *pInfo)
606 {
607  return static_cast<MIDIPlay*>(pInfo)->MidiTime();
608 }
609 
610 // Set up state to iterate NoteTrack events in sequence.
611 // Sends MIDI control changes up to the starting point mT0
612 // if send is true. Output is delayed by offset to facilitate
613 // looping (each iteration is delayed more).
614 void MIDIPlay::PrepareMidiIterator(bool send, double offset)
615 {
616  int i;
617  int nTracks = mMidiPlaybackTracks.size();
618  // instead of initializing with an Alg_seq, we use begin_seq()
619  // below to add ALL Alg_seq's.
620  mIterator.emplace(nullptr, false);
621  // Iterator not yet initialized, must add each track...
622  for (i = 0; i < nTracks; i++) {
623  const auto t = mMidiPlaybackTracks[i].get();
624  Alg_seq_ptr seq = &t->GetSeq();
625  // mark sequence tracks as "in use" since we're handing this
626  // off to another thread and want to make sure nothing happens
627  // to the data until playback finishes. This is just a sanity check.
628  seq->set_in_use(true);
629  mIterator->begin_seq(seq,
630  // casting away const, but allegro just uses the pointer as an opaque "cookie"
631  const_cast<NoteTrack*>(t),
632  t->GetOffset() + offset);
633  }
634  GetNextEvent(); // prime the pump for FillOtherBuffers
635 
636  // Start MIDI from current cursor position
637  mSendMidiState = true;
638  while (mNextEvent &&
639  mNextEventTime < mPlaybackSchedule.mT0 + offset) {
640  if (send) OutputEvent(0);
641  GetNextEvent();
642  }
643  mSendMidiState = false;
644 }
645 
647 {
648 #ifdef __WXGTK__
649  // Duplicating a bit of AudioIO::StartStream
650  // Detect whether ALSA is the chosen host, and do the various involved MIDI
651  // timing compensations only then.
652  mUsingAlsa = (AudioIOHost.Read() == L"ALSA");
653 #endif
654 
655  int i;
656  int nTracks = mMidiPlaybackTracks.size();
657  // Only start MIDI stream if there is an open track
658  if (nTracks == 0)
659  return false;
660 
661  //wxPrintf("StartPortMidiStream: mT0 %g mTime %g\n",
662  // mT0, mTime);
663 
664  /* get midi playback device */
665  PmDeviceID playbackDevice = Pm_GetDefaultOutputDeviceID();
666  auto playbackDeviceName = MIDIPlaybackDevice.Read();
668  if (wxStrcmp(playbackDeviceName, wxT("")) != 0) {
669  for (i = 0; i < Pm_CountDevices(); i++) {
670  const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
671  if (!info) continue;
672  if (!info->output) continue;
673  wxString interf = wxSafeConvertMB2WX(info->interf);
674  wxString name = wxSafeConvertMB2WX(info->name);
675  interf.Append(wxT(": ")).Append(name);
676  if (wxStrcmp(interf, playbackDeviceName) == 0) {
677  playbackDevice = i;
678  }
679  }
680  } // (else playback device has Pm_GetDefaultOuputDeviceID())
681 
682  if (playbackDevice < 0)
683  return false;
684 
685  /* open output device */
686  mLastPmError = Pm_OpenOutput(&mMidiStream,
687  playbackDevice,
688  NULL,
689  0,
690  &::MidiTime,
691  this, // supplies pInfo argument to MidiTime
693  if (mLastPmError == pmNoError) {
694  mMidiStreamActive = true;
695  mMidiPaused = false;
696  mMidiLoopPasses = 0;
697  mMidiOutputComplete = false;
698  mMaxMidiTimestamp = 0;
699  PrepareMidiIterator(true, 0);
700 
701  // It is ok to call this now, but do not send timestamped midi
702  // until after the first audio callback, which provides necessary
703  // data for MidiTime().
704  Pm_Synchronize(mMidiStream); // start using timestamps
705  }
706  return (mLastPmError == pmNoError);
707 }
708 
710 {
712  /* Stop Midi playback */
713  mMidiStreamActive = false;
714 
715  mMidiOutputComplete = true;
716 
717  // now we can assume "ownership" of the mMidiStream
718  // if output in progress, send all off, etc.
719  AllNotesOff();
720  // AllNotesOff() should be sufficient to stop everything, but
721  // in Linux, if you Pm_Close() immediately, it looks like
722  // messages are dropped. ALSA then seems to send All Sound Off
723  // and Reset All Controllers messages, but not all synthesizers
724  // respond to these messages. This is probably a bug in PortMidi
725  // if the All Off messages do not get out, but for security,
726  // delay a bit so that messages can be delivered before closing
727  // the stream. Add 2ms of "padding" to avoid any rounding errors.
728  while (mMaxMidiTimestamp + 2 > MidiTime()) {
729  using namespace std::chrono;
730  std::this_thread::sleep_for(1ms); // deliver the all-off messages
731  }
732  Pm_Close(mMidiStream);
733  mMidiStream = NULL;
734  mIterator->end();
735 
736  // set in_use flags to false
737  int nTracks = mMidiPlaybackTracks.size();
738  for (int i = 0; i < nTracks; i++) {
739  const auto t = mMidiPlaybackTracks[i].get();
740  Alg_seq_ptr seq = &t->GetSeq();
741  seq->set_in_use(false);
742  }
743 
744  mIterator.reset(); // just in case someone tries to reference it
745  }
746 
747  mMidiPlaybackTracks.clear();
748 }
749 
750 static Alg_update gAllNotesOff; // special event for loop ending
751 // the fields of this event are never used, only the address is important
752 
753 double MIDIPlay::UncorrectedMidiEventTime(double pauseTime)
754 {
755  double time;
757  time =
761  else
762  time = mNextEventTime;
763 
764  return time + pauseTime;
765 }
766 
767 void MIDIPlay::OutputEvent(double pauseTime)
768 {
769  int channel = (mNextEvent->chan) & 0xF; // must be in [0..15]
770  int command = -1;
771  int data1 = -1;
772  int data2 = -1;
773 
774  double eventTime = UncorrectedMidiEventTime(pauseTime);
775 
776  // 0.0005 is for rounding
777  double time = eventTime + 0.0005 -
778  (mSynthLatency * 0.001);
779 
780  time += 1; // MidiTime() has a 1s offset
781  // state changes have to go out without delay because the
782  // midi stream time gets reset when playback starts, and
783  // we don't want to leave any control changes scheduled for later
784  if (time < 0 || mSendMidiState) time = 0;
785  PmTimestamp timestamp = (PmTimestamp) (time * 1000); /* s to ms */
786 
787  // The special event gAllNotesOff means "end of playback, send
788  // all notes off on all channels"
789  if (mNextEvent == &gAllNotesOff) {
791  AllNotesOff(looping);
792  if (looping) {
793  // jump back to beginning of loop
794  ++mMidiLoopPasses;
796  } else {
797  mNextEvent = nullptr;
798  }
799  return;
800  }
801 
802  // if mNextEvent's channel is visible, play it, visibility can
803  // be updated while playing. Be careful: if we have a note-off,
804  // then we must not pay attention to the channel selection
805  // or mute/solo buttons because we must turn the note off
806  // even if the user changed something after the note began
807  // Note that because multiple tracks can output to the same
808  // MIDI channels, it is not a good idea to send "All Notes Off"
809  // when the user presses the mute button. We have no easy way
810  // to know what notes are sounding on any given muted track, so
811  // we'll just wait for the note-off events to happen.
812  // Also note that note-offs are only sent when we call
813  // mIterator->request_note_off(), so notes that are not played
814  // will not generate random note-offs. There is the interesting
815  // case that if the playback is paused, all-notes-off WILL be sent
816  // and if playback resumes, the pending note-off events WILL also
817  // be sent (but if that is a problem, there would also be a problem
818  // in the non-pause case.
819  if (((mNextEventTrack->IsVisibleChan(channel)) &&
820  // only play if note is not muted:
821  !((mHasSolo || mNextEventTrack->GetMute()) &&
822  !mNextEventTrack->GetSolo())) ||
823  (mNextEvent->is_note() && !mNextIsNoteOn)) {
824  // Note event
825  if (mNextEvent->is_note() && !mSendMidiState) {
826  // Pitch and velocity
827  data1 = mNextEvent->get_pitch();
828  if (mNextIsNoteOn) {
829  data2 = mNextEvent->get_loud(); // get velocity
830  int offset = mNextEventTrack->GetVelocity();
831  data2 += offset; // offset comes from per-track slider
832  // clip velocity to insure a legal note-on value
833  data2 = (data2 < 1 ? 1 : (data2 > 127 ? 127 : data2));
834  // since we are going to play this note, we need to get a note_off
835  mIterator->request_note_off();
836 
837 #ifdef AUDIO_IO_GB_MIDI_WORKAROUND
838  mPendingNotesOff.push_back(std::make_pair(channel, data1));
839 #endif
840  }
841  else {
842  data2 = 0; // 0 velocity means "note off"
843 #ifdef AUDIO_IO_GB_MIDI_WORKAROUND
844  auto end = mPendingNotesOff.end();
845  auto iter = std::find(
846  mPendingNotesOff.begin(), end, std::make_pair(channel, data1) );
847  if (iter != end)
848  mPendingNotesOff.erase(iter);
849 #endif
850  }
851  command = 0x90; // MIDI NOTE ON (or OFF when velocity == 0)
852  // Update event
853  } else if (mNextEvent->is_update()) {
854  // this code is based on allegrosmfwr.cpp -- it could be improved
855  // by comparing attribute pointers instead of string compares
856  Alg_update_ptr update = static_cast<Alg_update_ptr>(mNextEvent);
857  const char *name = update->get_attribute();
858 
859  if (!strcmp(name, "programi")) {
860  // Instrument change
861  data1 = update->parameter.i;
862  data2 = 0;
863  command = 0xC0; // MIDI PROGRAM CHANGE
864  } else if (!strncmp(name, "control", 7)) {
865  // Controller change
866 
867  // The number of the controller being changed is embedded
868  // in the parameter name.
869  data1 = atoi(name + 7);
870  // Allegro normalizes controller values
871  data2 = ROUND(update->parameter.r * 127);
872  command = 0xB0;
873  } else if (!strcmp(name, "bendr")) {
874  // Bend change
875 
876  // Reverse Allegro's post-processing of bend values
877  int temp = ROUND(0x2000 * (update->parameter.r + 1));
878  if (temp > 0x3fff) temp = 0x3fff; // 14 bits maximum
879  if (temp < 0) temp = 0;
880  data1 = temp & 0x7f; // low 7 bits
881  data2 = temp >> 7; // high 7 bits
882  command = 0xE0; // MIDI PITCH BEND
883  } else if (!strcmp(name, "pressurer")) {
884  // Pressure change
885  data1 = (int) (update->parameter.r * 127);
886  if (update->get_identifier() < 0) {
887  // Channel pressure
888  data2 = 0;
889  command = 0xD0; // MIDI CHANNEL PRESSURE
890  } else {
891  // Key pressure
892  data2 = data1;
893  data1 = update->get_identifier();
894  command = 0xA0; // MIDI POLY PRESSURE
895  }
896  }
897  }
898  if (command != -1) {
899  // keep track of greatest timestamp used
900  if (timestamp > mMaxMidiTimestamp) {
901  mMaxMidiTimestamp = timestamp;
902  }
903  Pm_WriteShort(mMidiStream, timestamp,
904  Pm_Message((int) (command + channel),
905  (long) data1, (long) data2));
906  /* wxPrintf("Pm_WriteShort %lx (%p) @ %d, advance %d\n",
907  Pm_Message((int) (command + channel),
908  (long) data1, (long) data2),
909  mNextEvent, timestamp, timestamp - Pt_Time()); */
910  }
911  }
912 }
913 
915 {
916  mNextEventTrack = nullptr; // clear it just to be safe
917  // now get the next event and the track from which it came
918  double nextOffset;
919  if (!mIterator) {
920  mNextEvent = nullptr;
921  return;
922  }
923  auto midiLoopOffset = MidiLoopOffset();
925  // Allegro retrieves the "cookie" for the event, which is a NoteTrack
926  reinterpret_cast<void **>(&mNextEventTrack),
927  &nextOffset, mPlaybackSchedule.mT1 + midiLoopOffset);
928 
929  mNextEventTime = mPlaybackSchedule.mT1 + midiLoopOffset + 1;
930  if (mNextEvent) {
932  mNextEvent->get_end_time()) + nextOffset;;
933  }
934  if (mNextEventTime > (mPlaybackSchedule.mT1 + midiLoopOffset)){ // terminate playback at mT1
936  mNextEventTime = mPlaybackSchedule.mT1 + midiLoopOffset - ALG_EPS;
937  mNextIsNoteOn = true; // do not look at duration
938  mIterator->end();
939  mIterator.reset(); // debugging aid
940  }
941 }
942 
943 
944 bool MIDIPlay::SetHasSolo(bool hasSolo)
945 {
946  mHasSolo = hasSolo;
947  return mHasSolo;
948 }
949 
950 
952  double rate, unsigned long pauseFrames, bool paused, bool hasSolo)
953 {
954  if (!mMidiStream)
955  return;
956 
957  // Keep track of time paused. If not paused, fill buffers.
958  if (paused) {
959  if (!mMidiPaused) {
960  mMidiPaused = true;
961  AllNotesOff(); // to avoid hanging notes during pause
962  }
963  return;
964  }
965 
966  if (mMidiPaused) {
967  mMidiPaused = false;
968  }
969 
970  SetHasSolo(hasSolo);
971 
972  // If we compute until mNextEventTime > current audio time,
973  // we would have a built-in compute-ahead of mAudioOutLatency, and
974  // it's probably good to compute MIDI when we compute audio (so when
975  // we stop, both stop about the same time).
976  double time = AudioTime(rate); // compute to here
977  // But if mAudioOutLatency is very low, we might need some extra
978  // compute-ahead to deal with mSynthLatency or even this thread.
979  double actual_latency = (MIDI_MINIMAL_LATENCY_MS + mSynthLatency) * 0.001;
980  if (actual_latency > mAudioOutLatency) {
981  time += actual_latency - mAudioOutLatency;
982  }
983  while (mNextEvent &&
984  UncorrectedMidiEventTime(PauseTime(rate, pauseFrames)) < time) {
985  OutputEvent(PauseTime(rate, pauseFrames));
986  GetNextEvent();
987  }
988 }
989 
990 double MIDIPlay::PauseTime(double rate, unsigned long pauseFrames)
991 {
992  return pauseFrames / rate;
993 }
994 
995 
996 // MidiTime() is an estimate in milliseconds of the current audio
997 // output (DAC) time + 1s. In other words, what audacity track time
998 // corresponds to the audio (including pause insertions) at the output?
999 //
1001 {
1002  // note: the extra 0.0005 is for rounding. Round down by casting to
1003  // unsigned long, then convert to PmTimeStamp (currently signed)
1004 
1005  // PRL: the time correction is really Midi latency achieved by different
1006  // means than specifying it to Pm_OpenStream. The use of the accumulated
1007  // sample count generated by the audio callback (in AudioTime()) might also
1008  // have the virtue of keeping the Midi output synched with audio.
1009 
1010  PmTimestamp ts;
1011  // subtract latency here because mSystemMinusAudioTime gets us
1012  // to the current *write* time, but we're writing ahead by audio output
1013  // latency (mAudioOutLatency).
1014  double now = SystemTime(mUsingAlsa);
1015  ts = (PmTimestamp) ((unsigned long)
1016  (1000 * (now + 1.0005 -
1018  // wxPrintf("AudioIO::MidiTime() %d time %g sys-aud %g\n",
1019  // ts, now, mSystemMinusAudioTime);
1020  return ts + MIDI_MINIMAL_LATENCY_MS;
1021 }
1022 
1023 
1024 void MIDIPlay::AllNotesOff(bool looping)
1025 {
1026 #ifdef __WXGTK__
1027  bool doDelay = !looping;
1028 #else
1029  bool doDelay = false;
1030  static_cast<void>(looping);// compiler food.
1031 #endif
1032 
1033  // to keep track of when MIDI should all be delivered,
1034  // update mMaxMidiTimestamp to now:
1035  PmTimestamp now = MidiTime();
1036  if (mMaxMidiTimestamp < now) {
1037  mMaxMidiTimestamp = now;
1038  }
1039 #ifdef AUDIO_IO_GB_MIDI_WORKAROUND
1040  // PRL:
1041  // Send individual note-off messages for each note-on not yet paired.
1042 
1043  // RBD:
1044  // Even this did not work as planned. My guess is ALSA does not use
1045  // a "stable sort" for timed messages, so that when a note-off is
1046  // added later at the same time as a future note-on, the order is
1047  // not respected, and the note-off can go first, leaving a stuck note.
1048  // The workaround here is to use mMaxMidiTimestamp to ensure that
1049  // note-offs come at least 1ms later than any previous message
1050 
1051  // PRL:
1052  // I think we should do that only when stopping or pausing, not when looping
1053  // Note that on Linux, MIDI always uses ALSA, no matter whether portaudio
1054  // uses some other host api.
1055 
1056  mMaxMidiTimestamp += 1;
1057  for (const auto &pair : mPendingNotesOff) {
1058  Pm_WriteShort(mMidiStream,
1059  (doDelay ? mMaxMidiTimestamp : 0),
1060  Pm_Message(
1061  0x90 + pair.first, pair.second, 0));
1062  mMaxMidiTimestamp++; // allow 1ms per note-off
1063  }
1064  mPendingNotesOff.clear();
1065 
1066  // Proceed to do the usual messages too.
1067 #endif
1068 
1069  for (int chan = 0; chan < 16; chan++) {
1070  Pm_WriteShort(mMidiStream,
1071  (doDelay ? mMaxMidiTimestamp : 0),
1072  Pm_Message(0xB0 + chan, 0x7B, 0));
1073  mMaxMidiTimestamp++; // allow 1ms per all-notes-off
1074  }
1075 }
1076 
1078  const PaStreamCallbackTimeInfo *timeInfo,
1079  unsigned long framesPerBuffer
1080  )
1081 {
1082  if (mCallbackCount++ == 0) {
1083  // This is effectively mSystemMinusAudioTime when the buffer is empty:
1085  // later, mStartTime - mSystemMinusAudioTime will tell us latency
1086  }
1087 
1088  /* for Linux, estimate a smooth audio time as a slowly-changing
1089  offset from system time */
1090  // rnow is system time as a double to simplify math
1091  double rnow = SystemTime(mUsingAlsa);
1092  // anow is next-sample-to-be-computed audio time as a double
1093  double anow = AudioTime(rate);
1094 
1095  if (mUsingAlsa) {
1096  // timeInfo's fields are not all reliable.
1097 
1098  // enow is audio time estimated from our clock synchronization protocol,
1099  // which produces mSystemMinusAudioTime. But we want the estimate
1100  // to drift low, so we steadily increase mSystemMinusAudioTime to
1101  // simulate a fast system clock or a slow audio clock. If anow > enow,
1102  // we'll update mSystemMinusAudioTime to keep in sync. (You might think
1103  // we could just use anow as the "truth", but it has a lot of jitter,
1104  // so we are using enow to smooth out this jitter, in fact to < 1ms.)
1105  // Add worst-case clock drift using previous framesPerBuffer:
1106  const auto increase =
1107  mAudioFramesPerBuffer * 0.0002 / rate;
1108  mSystemMinusAudioTime += increase;
1110  double enow = rnow - mSystemMinusAudioTime;
1111 
1112 
1113  // now, use anow instead if it is ahead of enow
1114  if (anow > enow) {
1115  mSystemMinusAudioTime = rnow - anow;
1116  // Update our mAudioOutLatency estimate during the first 20 callbacks.
1117  // During this period, the buffer should fill. Once we have a good
1118  // estimate of mSystemMinusAudioTime (expected in fewer than 20 callbacks)
1119  // we want to stop the updating in case there is clock drift, which would
1120  // cause the mAudioOutLatency estimation to drift as well. The clock drift
1121  // in the first 20 callbacks should be negligible, however.
1122  if (mCallbackCount < 20) {
1125  }
1128  }
1129  }
1130  else {
1131  // If not using Alsa, rely on timeInfo to have meaningful values that are
1132  // more precise than the output latency value reported at stream start.
1133  mSystemMinusAudioTime = rnow - anow;
1136  (timeInfo->outputBufferDacTime - timeInfo->currentTime);
1137  }
1138 
1139  mAudioFramesPerBuffer = framesPerBuffer;
1140  mNumFrames += framesPerBuffer;
1141 }
1142 
1144 {
1145  return std::count_if(
1147  [](const auto &pTrack){ return pTrack->GetSolo(); } );
1148 }
1149 
1151 {
1152  mMidiOutputComplete = true;
1153 }
1154 }
1155 
1157 {
1158  return ( mMidiStreamActive && !mMidiOutputComplete );
1159 }
1160 
1162 {
1163  return IsActive();
1164 }
1165 
1167 {
1168  return {
1169  wxT("mididev.txt"),
1171  wxT("MIDI Device Info")
1172  };
1173 }
1174 
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiLoopPasses
int mMidiLoopPasses
total of backward jumps
Definition: MIDIPlay.h:64
MIDIPlay.h
Inject added MIDI playback capability into Audacity's audio engine.
BasicUI::MessageBoxOptions
Definition: BasicUI.h:91
anonymous_namespace{MIDIPlay.h}::MIDIPlay::SignalOtherCompletion
void SignalOtherCompletion() override
Definition: MIDIPlay.cpp:1150
anonymous_namespace{MIDIPlay.cpp}::util_GetTime
static PaTime util_GetTime(void)
Definition: MIDIPlay.cpp:468
NoteTrack::GetOffset
double GetOffset() const override
Definition: NoteTrack.cpp:208
PlaybackSchedule::GetPolicy
PlaybackPolicy & GetPolicy()
Definition: PlaybackSchedule.cpp:136
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiStream
PmStream * mMidiStream
Definition: MIDIPlay.h:53
PlaybackSchedule::mT1
double mT1
Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 duri...
Definition: PlaybackSchedule.h:273
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiPaused
bool mMidiPaused
Definition: MIDIPlay.h:73
BasicUI::ShowMessageBox
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:248
anonymous_namespace{MIDIPlay.h}::MIDIPlay::Dump
AudioIODiagnostics Dump() const override
Get diagnostic information for audio devices and also for extensions.
Definition: MIDIPlay.cpp:1166
PlaybackSchedule::mT0
double mT0
Playback starts at offset of mT0, which is measured in seconds.
Definition: PlaybackSchedule.h:271
Track::SharedPointer
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:291
MIDISynthLatency_ms
IntSetting MIDISynthLatency_ms
Definition: NoteTrack.cpp:1275
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiOutputComplete
static bool mMidiOutputComplete
True when output reaches mT1.
Definition: MIDIPlay.h:48
NoteTrackConstArray
std::vector< std::shared_ptr< const NoteTrack > > NoteTrackConstArray
Definition: MIDIPlay.cpp:370
anonymous_namespace{MIDIPlay.h}::MIDIPlay::UncorrectedMidiEventTime
double UncorrectedMidiEventTime(double pauseTime)
Definition: MIDIPlay.cpp:753
anonymous_namespace{MIDIPlay.h}::MIDIPlay::AudioTime
double AudioTime(double rate) const
Definition: MIDIPlay.h:41
XO
#define XO(s)
Definition: Internat.h:31
PlaybackPolicy::Looping
virtual bool Looping(const PlaybackSchedule &schedule) const
Definition: PlaybackSchedule.cpp:125
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mStartTime
double mStartTime
Definition: MIDIPlay.h:91
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mSynthLatency
long mSynthLatency
Latency of MIDI synthesizer.
Definition: MIDIPlay.h:57
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mSystemMinusAudioTimePlusLatency
double mSystemMinusAudioTimePlusLatency
Definition: MIDIPlay.h:95
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mUsingAlsa
bool mUsingAlsa
Definition: MIDIPlay.h:149
anonymous_namespace{MIDIPlay.h}::MIDIPlay::~MIDIPlay
~MIDIPlay() override
Definition: MIDIPlay.cpp:545
anonymous_namespace{MIDIPlay.cpp}::gAllNotesOff
static Alg_update gAllNotesOff
Definition: MIDIPlay.cpp:750
TransportTracks::otherPlayableTracks
PlayableTrackConstArray otherPlayableTracks
Definition: AudioIO.h:67
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mPendingNotesOff
std::vector< std::pair< int, int > > mPendingNotesOff
Definition: MIDIPlay.h:102
Setting::Read
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:128
NoteTrack::IsVisibleChan
bool IsVisibleChan(int c) const
Definition: NoteTrack.h:174
anonymous_namespace{MIDIPlay.h}::MIDIPlay::MidiLoopOffset
double MidiLoopOffset()
Definition: MIDIPlay.h:66
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mNextIsNoteOn
bool mNextIsNoteOn
Is the next event a note-on?
Definition: MIDIPlay.h:111
MIDIPlay
Callbacks that AudioIO uses, to synchronize audio and MIDI playback.
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiStreamActive
static bool mMidiStreamActive
mMidiStreamActive tells when mMidiStream is open for output
Definition: MIDIPlay.h:51
anonymous_namespace{MIDIPlay.h}::MIDIPlay::AllNotesOff
void AllNotesOff(bool looping=false)
Definition: MIDIPlay.cpp:1024
anonymous_namespace{MIDIPlay.cpp}::MidiTime
PmTimestamp MidiTime(void *pInfo)
Definition: MIDIPlay.cpp:605
anonymous_namespace{MIDIPlay.h}::MIDIPlay::FillOtherBuffers
void FillOtherBuffers(double rate, unsigned long pauseFrames, bool paused, bool hasSolo) override
Definition: MIDIPlay.cpp:951
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mCallbackCount
long mCallbackCount
number of callbacks since stream start
Definition: MIDIPlay.h:93
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mNextEventTrack
NoteTrack * mNextEventTrack
Track of next event.
Definition: MIDIPlay.h:109
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mSystemMinusAudioTime
double mSystemMinusAudioTime
Definition: MIDIPlay.h:81
PlaybackSchedule
Definition: PlaybackSchedule.h:268
name
const TranslatableString name
Definition: Distortion.cpp:98
anonymous_namespace{MIDIPlay.h}::MIDIPlay::ComputeOtherTimings
void ComputeOtherTimings(double rate, const PaStreamCallbackTimeInfo *timeInfo, unsigned long framesPerBuffer) override
Definition: MIDIPlay.cpp:1077
anonymous_namespace{MIDIPlay.h}::MIDIPlay::CountOtherSoloTracks
unsigned CountOtherSoloTracks() const override
Definition: MIDIPlay.cpp:1143
AudioIODiagnostics
Definition: AudioIOBase.h:77
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiPlaybackTracks
NoteTrackConstArray mMidiPlaybackTracks
Definition: MIDIPlay.h:45
anonymous_namespace{MIDIPlay.h}::MIDIPlay::PrepareMidiIterator
void PrepareMidiIterator(bool send, double offset)
Definition: MIDIPlay.cpp:614
anonymous_namespace{MIDIPlay.h}::MIDIPlay::MidiTime
PmTimestamp MidiTime()
Compute the current PortMidi timestamp time.
Definition: MIDIPlay.cpp:1000
anonymous_namespace{MIDIPlay.h}::MIDIPlay::PauseTime
double PauseTime(double rate, unsigned long pauseFrames)
Definition: MIDIPlay.cpp:990
PmTimestamp
int32_t PmTimestamp
Definition: MIDIPlay.h:21
AudioIOExt::RegisteredFactory
Typically statically constructed.
Definition: AudioIOExt.h:37
anonymous_namespace{MIDIPlay.h}::MIDIPlay::StopOtherStream
void StopOtherStream() override
Definition: MIDIPlay.cpp:709
TransportTracks
Definition: AudioIO.h:64
anonymous_namespace{MIDIPlay.cpp}::SystemTime
static double SystemTime(bool usingAlsa)
Definition: MIDIPlay.cpp:487
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mNumFrames
long mNumFrames
Number of frames output, including pauses.
Definition: MIDIPlay.h:62
BasicUI.h
Toolkit-neutral facade for basic user interface services.
anonymous_namespace{MIDIPlay.h}::MIDIPlay::IsOtherStreamActive
bool IsOtherStreamActive() const override
Definition: MIDIPlay.cpp:1161
LAT1CTOWX
#define LAT1CTOWX(X)
Definition: Internat.h:160
anonymous_namespace{MIDIPlay.cpp}::streamStartTime
static double streamStartTime
Definition: MIDIPlay.cpp:485
BasicUI
Definition: Export.h:39
BasicUI::MessageBoxOptions::Caption
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:98
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mAudioFramesPerBuffer
long mAudioFramesPerBuffer
Definition: MIDIPlay.h:70
AudioIO.h
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mIterator
std::optional< Alg_iterator > mIterator
Definition: MIDIPlay.h:97
AudioIOHost
StringSetting AudioIOHost
Definition: AudioIOBase.cpp:933
anonymous_namespace{MIDIPlay.h}::MIDIPlay::StartOtherStream
bool StartOtherStream(const TransportTracks &tracks, const PaStreamInfo *info, double startTime, double rate) override
Definition: MIDIPlay.cpp:550
ROUND
#define ROUND(x)
Definition: MIDIPlay.cpp:367
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mSendMidiState
bool mSendMidiState
Definition: MIDIPlay.h:114
PlaybackSchedule::mWarpedLength
double mWarpedLength
Definition: PlaybackSchedule.h:288
Prefs.h
GetMIDIDeviceInfo
wxString GetMIDIDeviceInfo()
Definition: NoteTrack.cpp:1169
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mHasSolo
bool mHasSolo
Definition: MIDIPlay.h:144
anonymous_namespace{MIDIPlay.h}::MIDIPlay::SetHasSolo
bool SetHasSolo(bool hasSolo)
Definition: MIDIPlay.cpp:944
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMaxMidiTimestamp
PmTimestamp mMaxMidiTimestamp
Definition: MIDIPlay.h:76
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mNextEvent
Alg_event * mNextEvent
The next event to play (or null)
Definition: MIDIPlay.h:99
anonymous_namespace{MIDIPlay.h}::MIDIPlay::OutputEvent
void OutputEvent(double pauseTime)
Definition: MIDIPlay.cpp:767
anonymous_namespace{MIDIPlay.h}::MIDIPlay::AbortOtherStream
void AbortOtherStream() override
Definition: MIDIPlay.cpp:600
PlaybackSchedule::mEnvelope
const BoundedEnvelope * mEnvelope
Definition: PlaybackSchedule.h:298
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mPlaybackSchedule
const PlaybackSchedule & mPlaybackSchedule
Definition: MIDIPlay.h:44
anonymous_namespace{MIDIPlay.h}::MIDIPlay::GetNextEvent
void GetNextEvent()
Definition: MIDIPlay.cpp:914
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mNextEventTime
double mNextEventTime
Definition: MIDIPlay.h:107
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mLastPmError
int mLastPmError
Definition: MIDIPlay.h:54
PlaybackSchedule::RealDuration
double RealDuration(double trackTime1) const
Definition: PlaybackSchedule.cpp:344
anonymous_namespace{MIDIPlay.h}::MIDIPlay::mAudioOutLatency
double mAudioOutLatency
Definition: MIDIPlay.h:84
NoteTrack
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:67
anonymous_namespace{MIDIPlay.h}::MIDIPlay::IsActive
static bool IsActive()
Definition: MIDIPlay.cpp:1156
anonymous_namespace{MIDIPlay.h}::MIDIPlay::StartPortMidiStream
bool StartPortMidiStream(double rate)
Definition: MIDIPlay.cpp:646
anonymous_namespace{MIDIPlay.cpp}::MIDI_MINIMAL_LATENCY_MS
@ MIDI_MINIMAL_LATENCY_MS
Definition: MIDIPlay.cpp:481
anonymous_namespace{MIDIPlay.cpp}::sMIDIPlayFactory
AudioIOExt::RegisteredFactory sMIDIPlayFactory
Definition: MIDIPlay.cpp:507
MIDIPlaybackDevice
StringSetting MIDIPlaybackDevice
Definition: NoteTrack.cpp:1273