Audacity 3.2.0
Public Member Functions | Static Public Member Functions | Public Attributes | Static Public Attributes | List of all members
anonymous_namespace{MIDIPlay.h}::MIDIPlay Struct Reference

#include <MIDIPlay.h>

Inheritance diagram for anonymous_namespace{MIDIPlay.h}::MIDIPlay:
[legend]
Collaboration diagram for anonymous_namespace{MIDIPlay.h}::MIDIPlay:
[legend]

Public Member Functions

 MIDIPlay (const PlaybackSchedule &schedule)
 
 ~MIDIPlay () override
 
double AudioTime (double rate) const
 
double MidiLoopOffset ()
 
void PrepareMidiIterator (bool send, double startTime, double offset)
 
bool StartPortMidiStream (double rate)
 
double PauseTime (double rate, unsigned long pauseFrames)
 
void AllNotesOff (bool looping=false)
 
PmTimestamp MidiTime ()
 Compute the current PortMidi timestamp time. More...
 
bool IsOtherStreamActive () const override
 
void ComputeOtherTimings (double rate, bool paused, const PaStreamCallbackTimeInfo *timeInfo, unsigned long framesPerBuffer) override
 
void SignalOtherCompletion () override
 
unsigned CountOtherSolo () const override
 
bool StartOtherStream (const TransportSequences &tracks, const PaStreamInfo *info, double startTime, double rate) override
 
void AbortOtherStream () override
 
void FillOtherBuffers (double rate, unsigned long pauseFrames, bool paused, bool hasSolo) override
 
void StopOtherStream () override
 
AudioIODiagnostics Dump () const override
 Get diagnostic information for audio devices and also for extensions. More...
 
- Public Member Functions inherited from AudioIOExt
virtual ~AudioIOExt ()
 
virtual void ComputeOtherTimings (double rate, bool paused, const PaStreamCallbackTimeInfo *timeInfo, unsigned long framesPerBuffer)=0
 
virtual void SignalOtherCompletion ()=0
 
virtual unsigned CountOtherSolo () const =0
 
virtual bool StartOtherStream (const TransportSequences &tracks, const PaStreamInfo *info, double startTime, double rate)=0
 
virtual void AbortOtherStream ()=0
 
virtual void FillOtherBuffers (double rate, unsigned long pauseFrames, bool paused, bool hasSolo)=0
 
virtual void StopOtherStream ()=0
 
- Public Member Functions inherited from AudioIOExtBase
virtual ~AudioIOExtBase ()
 
virtual bool IsOtherStreamActive () const =0
 
virtual AudioIODiagnostics Dump () const =0
 Get diagnostic information for audio devices and also for extensions. More...
 

Static Public Member Functions

static bool IsActive ()
 
- Static Public Member Functions inherited from AudioIOExt
static FactoriesGetFactories ()
 

Public Attributes

const PlaybackSchedulemPlaybackSchedule
 
NoteTrackConstArray mMidiPlaybackTracks
 
PmStreammMidiStream = nullptr
 
int mLastPmError = 0
 
long mSynthLatency = MIDISynthLatency_ms.GetDefault()
 Latency of MIDI synthesizer. More...
 
long mNumFrames = 0
 Number of frames output, including pauses. More...
 
int mMidiLoopPasses = 0
 total of backward jumps More...
 
long mAudioFramesPerBuffer = 0
 
bool mMidiPaused = false
 
PmTimestamp mMaxMidiTimestamp = 0
 
double mSystemMinusAudioTime = 0.0
 
double mAudioOutLatency = 0.0
 
double mStartTime = 0.0
 
long mCallbackCount = 0
 number of callbacks since stream start More...
 
double mSystemMinusAudioTimePlusLatency = 0.0
 
std::optional< IteratormIterator
 
std::vector< std::pair< int, int > > mPendingNotesOff
 
bool mUsingAlsa = false
 

Static Public Attributes

static bool mMidiOutputComplete = true
 True when output reaches mT1. More...
 
static bool mMidiStreamActive = false
 mMidiStreamActive tells when mMidiStream is open for output More...
 

Additional Inherited Members

- Public Types inherited from AudioIOExt
using Factory = std::function< std::unique_ptr< AudioIOExt >(const PlaybackSchedule &) >
 
using Factories = std::vector< AudioIOExt::Factory >
 

Detailed Description

Definition at line 83 of file MIDIPlay.h.

Constructor & Destructor Documentation

◆ MIDIPlay()

MIDIPlay::MIDIPlay ( const PlaybackSchedule schedule)
explicit

Definition at line 516 of file MIDIPlay.cpp.

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}
XO("Cut/Copy/Paste")
#define LAT1CTOWX(X)
Definition: Internat.h:158
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
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:101
std::vector< std::pair< int, int > > mPendingNotesOff
Definition: MIDIPlay.h:147
const PlaybackSchedule & mPlaybackSchedule
Definition: MIDIPlay.h:91

References BasicUI::MessageBoxOptions::Caption(), LAT1CTOWX, mPendingNotesOff, BasicUI::ShowMessageBox(), and XO().

Here is the call graph for this function:

◆ ~MIDIPlay()

MIDIPlay::~MIDIPlay ( )
override

Definition at line 548 of file MIDIPlay.cpp.

549{
550 Pm_Terminate();
551}

Member Function Documentation

◆ AbortOtherStream()

void MIDIPlay::AbortOtherStream ( )
overridevirtual

Implements AudioIOExt.

Definition at line 603 of file MIDIPlay.cpp.

604{
605 mMidiPlaybackTracks.clear();
606}
NoteTrackConstArray mMidiPlaybackTracks
Definition: MIDIPlay.h:92

References mMidiPlaybackTracks.

◆ AllNotesOff()

void MIDIPlay::AllNotesOff ( bool  looping = false)

Definition at line 1063 of file MIDIPlay.cpp.

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}
int32_t PmTimestamp
Definition: MIDIPlay.h:20
PmTimestamp MidiTime()
Compute the current PortMidi timestamp time.
Definition: MIDIPlay.cpp:1039

References MidiTime(), mMaxMidiTimestamp, mMidiStream, and mPendingNotesOff.

Referenced by ComputeOtherTimings(), anonymous_namespace{MIDIPlay.h}::Iterator::OutputEvent(), and StopOtherStream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AudioTime()

double anonymous_namespace{MIDIPlay.h}::MIDIPlay::AudioTime ( double  rate) const
inline

Definition at line 88 of file MIDIPlay.h.

89 { return mPlaybackSchedule.mT0 + mNumFrames / rate; }
double mT0
Playback starts at offset of mT0, which is measured in seconds.
long mNumFrames
Number of frames output, including pauses.
Definition: MIDIPlay.h:109

Referenced by ComputeOtherTimings(), and FillOtherBuffers().

Here is the caller graph for this function:

◆ ComputeOtherTimings()

void MIDIPlay::ComputeOtherTimings ( double  rate,
bool  paused,
const PaStreamCallbackTimeInfo *  timeInfo,
unsigned long  framesPerBuffer 
)
overridevirtual

Implements AudioIOExt.

Definition at line 1116 of file MIDIPlay.cpp.

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}
static double SystemTime(bool usingAlsa)
Definition: MIDIPlay.cpp:486
long mCallbackCount
number of callbacks since stream start
Definition: MIDIPlay.h:140
double AudioTime(double rate) const
Definition: MIDIPlay.h:88
void AllNotesOff(bool looping=false)
Definition: MIDIPlay.cpp:1063

References AllNotesOff(), AudioTime(), mAudioFramesPerBuffer, mAudioOutLatency, mCallbackCount, mMidiPaused, mNumFrames, mPlaybackSchedule, mStartTime, mSystemMinusAudioTime, mSystemMinusAudioTimePlusLatency, PlaybackSchedule::mT0, mUsingAlsa, and anonymous_namespace{MIDIPlay.cpp}::SystemTime().

Here is the call graph for this function:

◆ CountOtherSolo()

unsigned MIDIPlay::CountOtherSolo ( ) const
overridevirtual

Implements AudioIOExt.

Definition at line 1192 of file MIDIPlay.cpp.

1193{
1194 return std::count_if(
1196 [](const auto &pTrack){ return pTrack->GetSolo(); } );
1197}

References mMidiPlaybackTracks.

◆ Dump()

AudioIODiagnostics MIDIPlay::Dump ( ) const
overridevirtual

Get diagnostic information for audio devices and also for extensions.

Implements AudioIOExtBase.

Definition at line 1215 of file MIDIPlay.cpp.

1216{
1217 return {
1218 wxT("mididev.txt"),
1220 wxT("MIDI Device Info")
1221 };
1222}
wxT("CloseDown"))
wxString GetMIDIDeviceInfo()
Definition: NoteTrack.cpp:890

References GetMIDIDeviceInfo(), and wxT().

Here is the call graph for this function:

◆ FillOtherBuffers()

void MIDIPlay::FillOtherBuffers ( double  rate,
unsigned long  pauseFrames,
bool  paused,
bool  hasSolo 
)
overridevirtual

Implements AudioIOExt.

Definition at line 991 of file MIDIPlay.cpp.

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}
virtual bool Looping(const PlaybackSchedule &schedule) const
PlaybackPolicy & GetPolicy()
std::optional< Iterator > mIterator
Definition: MIDIPlay.h:144
long mSynthLatency
Latency of MIDI synthesizer.
Definition: MIDIPlay.h:104
void PrepareMidiIterator(bool send, double startTime, double offset)
Definition: MIDIPlay.cpp:617
int mMidiLoopPasses
total of backward jumps
Definition: MIDIPlay.h:111
double PauseTime(double rate, unsigned long pauseFrames)
Definition: MIDIPlay.cpp:1029

References AudioTime(), PlaybackSchedule::GetPolicy(), PlaybackPolicy::Looping(), mAudioOutLatency, anonymous_namespace{MIDIPlay.cpp}::MIDI_MINIMAL_LATENCY_MS, MidiLoopOffset(), mIterator, mMidiLoopPasses, mMidiStream, mPlaybackSchedule, mSynthLatency, PlaybackSchedule::mT0, PauseTime(), and PrepareMidiIterator().

Here is the call graph for this function:

◆ IsActive()

bool MIDIPlay::IsActive ( )
static

Definition at line 1205 of file MIDIPlay.cpp.

1206{
1208}
static bool mMidiStreamActive
mMidiStreamActive tells when mMidiStream is open for output
Definition: MIDIPlay.h:98
static bool mMidiOutputComplete
True when output reaches mT1.
Definition: MIDIPlay.h:95

◆ IsOtherStreamActive()

bool MIDIPlay::IsOtherStreamActive ( ) const
overridevirtual

Implements AudioIOExtBase.

Definition at line 1210 of file MIDIPlay.cpp.

1211{
1212 return IsActive();
1213}

◆ MidiLoopOffset()

double anonymous_namespace{MIDIPlay.h}::MIDIPlay::MidiLoopOffset ( )
inline

Definition at line 113 of file MIDIPlay.h.

113 {
115 }
double mT1
Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 duri...

References PlaybackSchedule::mT0, and PlaybackSchedule::mT1.

Referenced by FillOtherBuffers(), anonymous_namespace{MIDIPlay.h}::Iterator::GetNextEvent(), and anonymous_namespace{MIDIPlay.h}::Iterator::UncorrectedMidiEventTime().

Here is the caller graph for this function:

◆ MidiTime()

PmTimestamp MIDIPlay::MidiTime ( )

Compute the current PortMidi timestamp time.

This is used by PortMidi to synchronize midi time to audio samples

Definition at line 1039 of file MIDIPlay.cpp.

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}

References anonymous_namespace{MIDIPlay.cpp}::MIDI_MINIMAL_LATENCY_MS, mSystemMinusAudioTimePlusLatency, mUsingAlsa, and anonymous_namespace{MIDIPlay.cpp}::SystemTime().

Referenced by AllNotesOff(), StartPortMidiStream(), and StopOtherStream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ PauseTime()

double MIDIPlay::PauseTime ( double  rate,
unsigned long  pauseFrames 
)

Definition at line 1029 of file MIDIPlay.cpp.

1030{
1031 return pauseFrames / rate;
1032}

Referenced by FillOtherBuffers().

Here is the caller graph for this function:

◆ PrepareMidiIterator()

void MIDIPlay::PrepareMidiIterator ( bool  send,
double  startTime,
double  offset 
)

Definition at line 617 of file MIDIPlay.cpp.

618{
619 mIterator.emplace(mPlaybackSchedule, *this,
620 mMidiPlaybackTracks, startTime, offset, send);
621}

References mIterator, mMidiPlaybackTracks, and mPlaybackSchedule.

Referenced by FillOtherBuffers(), and StartPortMidiStream().

Here is the caller graph for this function:

◆ SignalOtherCompletion()

void MIDIPlay::SignalOtherCompletion ( )
overridevirtual

Implements AudioIOExt.

Definition at line 1199 of file MIDIPlay.cpp.

1200{
1201 mMidiOutputComplete = true;
1202}

References mMidiOutputComplete.

◆ StartOtherStream()

bool MIDIPlay::StartOtherStream ( const TransportSequences tracks,
const PaStreamInfo *  info,
double  startTime,
double  rate 
)
overridevirtual

Implements AudioIOExt.

Definition at line 553 of file MIDIPlay.cpp.

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}
const auto tracks
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:78

References mAudioFramesPerBuffer, mAudioOutLatency, mCallbackCount, mMidiPlaybackTracks, mNumFrames, mSystemMinusAudioTime, mSystemMinusAudioTimePlusLatency, mUsingAlsa, StartPortMidiStream(), anonymous_namespace{MIDIPlay.cpp}::streamStartTime, anonymous_namespace{MIDIPlay.cpp}::SystemTime(), and tracks.

Here is the call graph for this function:

◆ StartPortMidiStream()

bool MIDIPlay::StartPortMidiStream ( double  rate)

Definition at line 678 of file MIDIPlay.cpp.

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}
StringSetting AudioIOHost
IntSetting MIDISynthLatency_ms
Definition: NoteTrack.cpp:991
StringSetting MIDIPlaybackDevice
Definition: NoteTrack.cpp:989
wxString name
Definition: TagsEditor.cpp:166
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:207

References AudioIOHost, anonymous_namespace{MIDIPlay.cpp}::MIDI_MINIMAL_LATENCY_MS, MIDIPlaybackDevice, MIDISynthLatency_ms, MidiTime(), mLastPmError, mMaxMidiTimestamp, mMidiLoopPasses, mMidiOutputComplete, mMidiPaused, mMidiPlaybackTracks, mMidiStream, mMidiStreamActive, mPlaybackSchedule, mSynthLatency, PlaybackSchedule::mT0, mUsingAlsa, name, PrepareMidiIterator(), Setting< T >::Read(), and wxT().

Referenced by StartOtherStream().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ StopOtherStream()

void MIDIPlay::StopOtherStream ( )
overridevirtual

Implements AudioIOExt.

Definition at line 741 of file MIDIPlay.cpp.

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}

References AllNotesOff(), MidiTime(), mIterator, mMaxMidiTimestamp, mMidiOutputComplete, mMidiPlaybackTracks, mMidiStream, and mMidiStreamActive.

Here is the call graph for this function:

Member Data Documentation

◆ mAudioFramesPerBuffer

long anonymous_namespace{MIDIPlay.h}::MIDIPlay::mAudioFramesPerBuffer = 0

Definition at line 117 of file MIDIPlay.h.

Referenced by ComputeOtherTimings(), and StartOtherStream().

◆ mAudioOutLatency

double anonymous_namespace{MIDIPlay.h}::MIDIPlay::mAudioOutLatency = 0.0

audio output latency reported by PortAudio (initially; for Alsa, we adjust it to the largest "observed" value)

Definition at line 131 of file MIDIPlay.h.

Referenced by ComputeOtherTimings(), FillOtherBuffers(), and StartOtherStream().

◆ mCallbackCount

long anonymous_namespace{MIDIPlay.h}::MIDIPlay::mCallbackCount = 0

number of callbacks since stream start

Definition at line 140 of file MIDIPlay.h.

Referenced by ComputeOtherTimings(), and StartOtherStream().

◆ mIterator

std::optional<Iterator> anonymous_namespace{MIDIPlay.h}::MIDIPlay::mIterator

Definition at line 144 of file MIDIPlay.h.

Referenced by FillOtherBuffers(), PrepareMidiIterator(), and StopOtherStream().

◆ mLastPmError

int anonymous_namespace{MIDIPlay.h}::MIDIPlay::mLastPmError = 0

Definition at line 101 of file MIDIPlay.h.

Referenced by StartPortMidiStream().

◆ mMaxMidiTimestamp

PmTimestamp anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMaxMidiTimestamp = 0

The largest timestamp written so far, used to delay stream closing until last message has been delivered

Definition at line 123 of file MIDIPlay.h.

Referenced by AllNotesOff(), anonymous_namespace{MIDIPlay.h}::Iterator::OutputEvent(), StartPortMidiStream(), and StopOtherStream().

◆ mMidiLoopPasses

int anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiLoopPasses = 0

◆ mMidiOutputComplete

bool MIDIPlay::mMidiOutputComplete = true
static

True when output reaches mT1.

Definition at line 95 of file MIDIPlay.h.

Referenced by SignalOtherCompletion(), StartPortMidiStream(), and StopOtherStream().

◆ mMidiPaused

bool anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiPaused = false

Used by Midi process to record that pause has begun, so that AllNotesOff() is only delivered once

Definition at line 120 of file MIDIPlay.h.

Referenced by ComputeOtherTimings(), and StartPortMidiStream().

◆ mMidiPlaybackTracks

NoteTrackConstArray anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiPlaybackTracks

◆ mMidiStream

PmStream* anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiStream = nullptr

◆ mMidiStreamActive

bool MIDIPlay::mMidiStreamActive = false
static

mMidiStreamActive tells when mMidiStream is open for output

Definition at line 98 of file MIDIPlay.h.

Referenced by StartPortMidiStream(), and StopOtherStream().

◆ mNumFrames

long anonymous_namespace{MIDIPlay.h}::MIDIPlay::mNumFrames = 0

Number of frames output, including pauses.

Definition at line 109 of file MIDIPlay.h.

Referenced by ComputeOtherTimings(), and StartOtherStream().

◆ mPendingNotesOff

std::vector< std::pair< int, int > > anonymous_namespace{MIDIPlay.h}::MIDIPlay::mPendingNotesOff

◆ mPlaybackSchedule

const PlaybackSchedule& anonymous_namespace{MIDIPlay.h}::MIDIPlay::mPlaybackSchedule

◆ mStartTime

double anonymous_namespace{MIDIPlay.h}::MIDIPlay::mStartTime = 0.0

time of first callback used to find "observed" latency

Definition at line 138 of file MIDIPlay.h.

Referenced by ComputeOtherTimings().

◆ mSynthLatency

long anonymous_namespace{MIDIPlay.h}::MIDIPlay::mSynthLatency = MIDISynthLatency_ms.GetDefault()

Latency of MIDI synthesizer.

Definition at line 104 of file MIDIPlay.h.

Referenced by FillOtherBuffers(), anonymous_namespace{MIDIPlay.h}::Iterator::OutputEvent(), and StartPortMidiStream().

◆ mSystemMinusAudioTime

double anonymous_namespace{MIDIPlay.h}::MIDIPlay::mSystemMinusAudioTime = 0.0

Offset from ideal sample computation time to system time, where "ideal" means when we would get the callback if there were no scheduling delays or computation time

Definition at line 128 of file MIDIPlay.h.

Referenced by ComputeOtherTimings(), and StartOtherStream().

◆ mSystemMinusAudioTimePlusLatency

double anonymous_namespace{MIDIPlay.h}::MIDIPlay::mSystemMinusAudioTimePlusLatency = 0.0

Definition at line 142 of file MIDIPlay.h.

Referenced by ComputeOtherTimings(), MidiTime(), and StartOtherStream().

◆ mUsingAlsa

bool anonymous_namespace{MIDIPlay.h}::MIDIPlay::mUsingAlsa = false

Definition at line 161 of file MIDIPlay.h.

Referenced by ComputeOtherTimings(), MidiTime(), StartOtherStream(), and StartPortMidiStream().


The documentation for this struct was generated from the following files: