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

#include <MIDIPlay.h>

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

Public Member Functions

 Iterator (const PlaybackSchedule &schedule, MIDIPlay &midiPlay, NoteTrackConstArray &midiPlaybackTracks, double startTime, double offset, bool send)
 
 ~Iterator ()
 
void Prime (bool send, double startTime)
 
double GetNextEventTime () const
 
double UncorrectedMidiEventTime (double pauseTime)
 
bool Unmuted (bool hasSolo) const
 
bool OutputEvent (double pauseTime, bool sendMidiState, bool hasSolo)
 
void GetNextEvent ()
 

Public Attributes

const PlaybackSchedulemPlaybackSchedule
 
MIDIPlaymMIDIPlay
 
Alg_iterator it { nullptr, false }
 
Alg_event * mNextEvent = nullptr
 The next event to play (or null) More...
 
NoteTrackmNextEventTrack = nullptr
 Track of next event. More...
 
bool mNextIsNoteOn = false
 Is the next event a note-on? More...
 

Private Attributes

double mNextEventTime = 0
 

Detailed Description

Definition at line 40 of file MIDIPlay.h.

Constructor & Destructor Documentation

◆ Iterator()

anonymous_namespace{MIDIPlay.cpp}::Iterator::Iterator ( const PlaybackSchedule schedule,
MIDIPlay midiPlay,
NoteTrackConstArray midiPlaybackTracks,
double  startTime,
double  offset,
bool  send 
)

Definition at line 623 of file MIDIPlay.cpp.

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}
const PlaybackSchedule & mPlaybackSchedule
Definition: MIDIPlay.h:65
void Prime(bool send, double startTime)
Definition: MIDIPlay.cpp:652

References it, and Prime().

Here is the call graph for this function:

◆ ~Iterator()

anonymous_namespace{MIDIPlay.cpp}::Iterator::~Iterator ( )

Definition at line 647 of file MIDIPlay.cpp.

648{
649 it.end();
650}

References it.

Member Function Documentation

◆ GetNextEvent()

void anonymous_namespace{MIDIPlay.cpp}::Iterator::GetNextEvent ( )

Definition at line 968 of file MIDIPlay.cpp.

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}
double mT1
Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 duri...
NoteTrack * mNextEventTrack
Track of next event.
Definition: MIDIPlay.h:72
bool mNextIsNoteOn
Is the next event a note-on?
Definition: MIDIPlay.h:75
Alg_event * mNextEvent
The next event to play (or null)
Definition: MIDIPlay.h:69

References anonymous_namespace{MIDIPlay.h}::gAllNotesOff, it, anonymous_namespace{MIDIPlay.h}::MIDIPlay::MidiLoopOffset(), mMIDIPlay, mNextEvent, mNextEventTime, mNextEventTrack, mNextIsNoteOn, mPlaybackSchedule, and PlaybackSchedule::mT1.

Referenced by Prime().

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

◆ GetNextEventTime()

double anonymous_namespace{MIDIPlay.cpp}::Iterator::GetNextEventTime ( ) const

Definition at line 671 of file MIDIPlay.cpp.

672{
673 if (mNextEvent == &gAllNotesOff)
674 return mNextEventTime - ALG_EPS;
675 return mNextEventTime;
676}

References anonymous_namespace{MIDIPlay.h}::gAllNotesOff, mNextEvent, and mNextEventTime.

Referenced by Prime(), and UncorrectedMidiEventTime().

Here is the caller graph for this function:

◆ OutputEvent()

bool anonymous_namespace{MIDIPlay.cpp}::Iterator::OutputEvent ( double  pauseTime,
bool  sendMidiState,
bool  hasSolo 
)
Parameters
sendMidiStatewhen true, sendMidiState means send only updates, not note-ons, used to send state changes that precede the selected notes

Definition at line 806 of file MIDIPlay.cpp.

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}
#define ROUND(x)
Definition: MIDIPlay.cpp:366
int32_t PmTimestamp
Definition: MIDIPlay.h:20
wxString name
Definition: TagsEditor.cpp:166
float GetVelocity() const
Definition: NoteTrack.h:125
virtual bool Looping(const PlaybackSchedule &schedule) const
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
PlaybackPolicy & GetPolicy()
bool Unmuted(bool hasSolo) const
Definition: MIDIPlay.cpp:795
double UncorrectedMidiEventTime(double pauseTime)
Definition: MIDIPlay.cpp:780
std::vector< std::pair< int, int > > mPendingNotesOff
Definition: MIDIPlay.h:147
long mSynthLatency
Latency of MIDI synthesizer.
Definition: MIDIPlay.h:104
void AllNotesOff(bool looping=false)
Definition: MIDIPlay.cpp:1063

References anonymous_namespace{MIDIPlay.h}::MIDIPlay::AllNotesOff(), details::end(), anonymous_namespace{MIDIPlay.h}::gAllNotesOff, PlaybackSchedule::GetPolicy(), NoteTrack::GetVelocity(), it, PlaybackPolicy::Looping(), anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMaxMidiTimestamp, mMIDIPlay, anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiStream, mNextEvent, mNextEventTrack, mNextIsNoteOn, anonymous_namespace{MIDIPlay.h}::MIDIPlay::mPendingNotesOff, mPlaybackSchedule, anonymous_namespace{MIDIPlay.h}::MIDIPlay::mSynthLatency, name, ROUND, UncorrectedMidiEventTime(), and Unmuted().

Referenced by Prime().

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

◆ Prime()

void anonymous_namespace{MIDIPlay.cpp}::Iterator::Prime ( bool  send,
double  startTime 
)

Definition at line 652 of file MIDIPlay.cpp.

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}
bool OutputEvent(double pauseTime, bool sendMidiState, bool hasSolo)
Definition: MIDIPlay.cpp:806

References GetNextEvent(), GetNextEventTime(), mNextEvent, and OutputEvent().

Referenced by Iterator().

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

◆ UncorrectedMidiEventTime()

double anonymous_namespace{MIDIPlay.cpp}::Iterator::UncorrectedMidiEventTime ( double  pauseTime)

Definition at line 780 of file MIDIPlay.cpp.

781{
782 double time;
784 time =
789 else
790 time = GetNextEventTime();
791
792 return time + pauseTime;
793}
double mT0
Playback starts at offset of mT0, which is measured in seconds.
const BoundedEnvelope * mEnvelope
double RealDuration(double trackTime1) const
int mMidiLoopPasses
total of backward jumps
Definition: MIDIPlay.h:111

References GetNextEventTime(), PlaybackSchedule::mEnvelope, anonymous_namespace{MIDIPlay.h}::MIDIPlay::MidiLoopOffset(), anonymous_namespace{MIDIPlay.h}::MIDIPlay::mMidiLoopPasses, mMIDIPlay, mPlaybackSchedule, PlaybackSchedule::mT0, PlaybackSchedule::mWarpedLength, and PlaybackSchedule::RealDuration().

Referenced by OutputEvent().

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

◆ Unmuted()

bool anonymous_namespace{MIDIPlay.cpp}::Iterator::Unmuted ( bool  hasSolo) const

Definition at line 795 of file MIDIPlay.cpp.

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}
bool IsVisibleChan(int c) const
Definition: NoteTrack.h:156
bool GetSolo() const
Definition: PlayableTrack.h:48
bool GetMute() const
Definition: PlayableTrack.h:47

References PlayableTrack::GetMute(), PlayableTrack::GetSolo(), NoteTrack::IsVisibleChan(), mNextEvent, and mNextEventTrack.

Referenced by OutputEvent().

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

Member Data Documentation

◆ it

Alg_iterator anonymous_namespace{MIDIPlay.h}::Iterator::it { nullptr, false }

Definition at line 67 of file MIDIPlay.h.

Referenced by GetNextEvent(), Iterator(), OutputEvent(), and ~Iterator().

◆ mMIDIPlay

MIDIPlay& anonymous_namespace{MIDIPlay.h}::Iterator::mMIDIPlay

Definition at line 66 of file MIDIPlay.h.

Referenced by GetNextEvent(), OutputEvent(), and UncorrectedMidiEventTime().

◆ mNextEvent

Alg_event* anonymous_namespace{MIDIPlay.h}::Iterator::mNextEvent = nullptr

The next event to play (or null)

Definition at line 69 of file MIDIPlay.h.

Referenced by GetNextEvent(), GetNextEventTime(), OutputEvent(), Prime(), and Unmuted().

◆ mNextEventTime

double anonymous_namespace{MIDIPlay.h}::Iterator::mNextEventTime = 0
private

Real time at which the next event should be output, measured in seconds. Note that this could be a note's time+duration for note offs.

Definition at line 80 of file MIDIPlay.h.

Referenced by GetNextEvent(), and GetNextEventTime().

◆ mNextEventTrack

NoteTrack* anonymous_namespace{MIDIPlay.h}::Iterator::mNextEventTrack = nullptr

Track of next event.

Definition at line 72 of file MIDIPlay.h.

Referenced by GetNextEvent(), OutputEvent(), and Unmuted().

◆ mNextIsNoteOn

bool anonymous_namespace{MIDIPlay.h}::Iterator::mNextIsNoteOn = false

Is the next event a note-on?

Definition at line 75 of file MIDIPlay.h.

Referenced by GetNextEvent(), and OutputEvent().

◆ mPlaybackSchedule

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

Definition at line 65 of file MIDIPlay.h.

Referenced by GetNextEvent(), OutputEvent(), and UncorrectedMidiEventTime().


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