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