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

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->GetStartTime() + offset);
644 }
645 Prime(send, startTime + offset);
646}
const PlaybackSchedule & mPlaybackSchedule
Definition: MIDIPlay.h:65
void Prime(bool send, double startTime)
Definition: MIDIPlay.cpp:653

References it, and Prime().

Here is the call graph for this function:

◆ ~Iterator()

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

Definition at line 648 of file MIDIPlay.cpp.

649{
650 it.end();
651}

References it.

Member Function Documentation

◆ GetNextEvent()

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

Definition at line 969 of file MIDIPlay.cpp.

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

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

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

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}
const TranslatableString name
Definition: Distortion.cpp:76
#define ROUND(x)
Definition: MIDIPlay.cpp:367
int32_t PmTimestamp
Definition: MIDIPlay.h:20
virtual bool Looping(const PlaybackSchedule &schedule) const
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
PlaybackPolicy & GetPolicy()
bool Unmuted(bool hasSolo) const
Definition: MIDIPlay.cpp:796
double UncorrectedMidiEventTime(double pauseTime)
Definition: MIDIPlay.cpp:781
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:1064

References anonymous_namespace{MIDIPlay.h}::MIDIPlay::AllNotesOff(), PackedArray::end(), anonymous_namespace{MIDIPlay.h}::gAllNotesOff, PlaybackSchedule::GetPolicy(), 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 653 of file MIDIPlay.cpp.

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

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

782{
783 double time;
785 time =
790 else
791 time = GetNextEventTime();
792
793 return time + pauseTime;
794}
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 796 of file MIDIPlay.cpp.

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}
bool IsVisibleChan(int c) const
Definition: NoteTrack.h:165

References 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: