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