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 CountOtherSoloTracks () const override
 
bool StartOtherStream (const TransportTracks &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 CountOtherSoloTracks () const =0
 
virtual bool StartOtherStream (const TransportTracks &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 517 of file MIDIPlay.cpp.

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}
XO("Cut/Copy/Paste")
#define LAT1CTOWX(X)
Definition: Internat.h:160
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
MessageBoxOptions && Caption(TranslatableString caption_) &&
Definition: BasicUI.h:98
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 549 of file MIDIPlay.cpp.

550{
551 Pm_Terminate();
552}

Member Function Documentation

◆ AbortOtherStream()

void MIDIPlay::AbortOtherStream ( )
overridevirtual

Implements AudioIOExt.

Definition at line 604 of file MIDIPlay.cpp.

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

References mMidiPlaybackTracks.

◆ AllNotesOff()

void MIDIPlay::AllNotesOff ( bool  looping = false)

Definition at line 1064 of file MIDIPlay.cpp.

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

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 1117 of file MIDIPlay.cpp.

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}
static double SystemTime(bool usingAlsa)
Definition: MIDIPlay.cpp:487
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:1064

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:

◆ CountOtherSoloTracks()

unsigned MIDIPlay::CountOtherSoloTracks ( ) const
overridevirtual

Implements AudioIOExt.

Definition at line 1193 of file MIDIPlay.cpp.

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

References mMidiPlaybackTracks.

◆ Dump()

AudioIODiagnostics MIDIPlay::Dump ( ) const
overridevirtual

Get diagnostic information for audio devices and also for extensions.

Implements AudioIOExtBase.

Definition at line 1216 of file MIDIPlay.cpp.

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

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 992 of file MIDIPlay.cpp.

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}
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:618
int mMidiLoopPasses
total of backward jumps
Definition: MIDIPlay.h:111
double PauseTime(double rate, unsigned long pauseFrames)
Definition: MIDIPlay.cpp:1030

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 1206 of file MIDIPlay.cpp.

1207{
1209}
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 1211 of file MIDIPlay.cpp.

1212{
1213 return IsActive();
1214}

◆ 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 1040 of file MIDIPlay.cpp.

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}

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 1030 of file MIDIPlay.cpp.

1031{
1032 return pauseFrames / rate;
1033}

Referenced by FillOtherBuffers().

Here is the caller graph for this function:

◆ PrepareMidiIterator()

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

Definition at line 618 of file MIDIPlay.cpp.

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

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 1200 of file MIDIPlay.cpp.

1201{
1202 mMidiOutputComplete = true;
1203}

References mMidiOutputComplete.

◆ StartOtherStream()

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

Implements AudioIOExt.

Definition at line 554 of file MIDIPlay.cpp.

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}
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:63
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:297
PlayableTrackConstArray otherPlayableTracks
Definition: AudioIO.h:89

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

Here is the call graph for this function:

◆ StartPortMidiStream()

bool MIDIPlay::StartPortMidiStream ( double  rate)

Definition at line 679 of file MIDIPlay.cpp.

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}
StringSetting AudioIOHost
const TranslatableString name
Definition: Distortion.cpp:74
IntSetting MIDISynthLatency_ms
Definition: NoteTrack.cpp:1286
StringSetting MIDIPlaybackDevice
Definition: NoteTrack.cpp:1284
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200

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 742 of file MIDIPlay.cpp.

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}

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: