void MIDISequencer::ScanEventsAtThisTime() { // save the current iterator state MIDIMultiTrackIteratorState istate ( state.iterator.GetState() ); int prev_measure = state.cur_measure; int prev_beat = state.cur_beat; // process all messages up to and including this time only MIDIClockTime orig_clock = state.cur_clock; double orig_time_ms = state.cur_time_ms; MIDIClockTime t = 0; int trk; MIDITimedBigMessage ev; while ( GetNextEventTime ( &t ) && t == orig_clock && GetNextEvent ( &trk, &ev ) ) { ; } // restore the iterator state state.iterator.SetState ( istate ); // and current time state.cur_clock = orig_clock; state.cur_time_ms = float ( orig_time_ms ); state.cur_measure = prev_measure; state.cur_beat = prev_beat; }
bool MIDISequencer::GetNextEventTimeMs ( double *t ) { MIDIClockTime ct; bool f = GetNextEventTime ( &ct ); if ( f ) { // calculate delta time from last event time double delta_clocks = ( double ) ( ct - state.cur_clock ); // calculate tempo in milliseconds per clock double clocks_per_sec = ( ( state.track_state[0]->tempobpm * ( ( ( double ) tempo_scale ) * 0.01 ) * ( 1. / 60. ) ) * state.multitrack->GetClksPerBeat() ); if ( clocks_per_sec > 0. ) { double ms_per_clock = 1000. / clocks_per_sec; // calculate delta time in milliseconds double delta_ms = delta_clocks * ms_per_clock; // return it added with the current time in ms. *t = delta_ms + state.cur_time_ms; } else { f = false; } } return f; }
bool MIDISequencer::GoToTime ( MIDIClockTime time_clk ) { // temporarily disable the gui notifier bool notifier_mode = false; if ( state.notifier ) { notifier_mode = state.notifier->GetEnable(); state.notifier->SetEnable ( false ); } if ( time_clk < state.cur_clock || time_clk == 0 ) { // start from zero if desired time is before where we are for ( int i = 0; i < state.num_tracks; ++i ) { state.track_state[i]->GoToZero(); } state.iterator.GoToTime ( 0 ); state.cur_time_ms = 0.0; state.cur_clock = 0; // state.next_beat_time = state.multitrack->GetClksPerBeat(); state.next_beat_time = state.multitrack->GetClksPerBeat() * 4 / ( state.track_state[0]->timesig_denominator ); state.cur_beat = 0; state.cur_measure = 0; } MIDIClockTime t = 0; int trk; MIDITimedBigMessage ev; while ( GetNextEventTime ( &t ) && t < time_clk && GetNextEvent ( &trk, &ev ) ) { ; } // examine all the events at this specific time // and update the track states to reflect this time ScanEventsAtThisTime(); // re-enable the gui notifier if it was enabled previously if ( state.notifier ) { state.notifier->SetEnable ( notifier_mode ); // cause a full gui refresh now state.notifier->Notify ( this, MIDISequencerGUIEvent::GROUP_ALL ); } return true; }
bool MIDISequencer::GoToMeasure( int measure, int beat ) { // temporarily disable the gui notifier bool notifier_mode=false; if( state.notifier ) { notifier_mode = state.notifier->GetEnable(); state.notifier->SetEnable(false); } if( measure < state.cur_measure || measure==0 ) { for( int i=0; i<state.num_tracks; ++i ) { state.track_state[i]->GoToZero(); } state.iterator.GoToTime( 0 ); state.cur_time_ms = 0.0; state.cur_clock = 0; state.cur_beat = 0; state.cur_measure = 0; // state.next_beat_time = state.multitrack->GetClksPerBeat(); state.next_beat_time = state.multitrack->GetClksPerBeat() * 4 / (state.track_state[0]->timesig_denominator); } MIDIClockTime t=0; int trk; MIDITimedBigMessage ev; // iterate thru all the events until cur-measure and cur_beat are // where we want them. while( GetNextEventTime( &t ) && GetNextEvent(&trk,&ev) && state.cur_measure<=measure ) { if( state.cur_measure==measure && state.cur_beat>=beat ) { break; } } // examine all the events at this specific time // and update the track states to reflect this time ScanEventsAtThisTime(); // re-enable the gui notifier if it was enabled previously if( state.notifier ) { state.notifier->SetEnable( notifier_mode ); // cause a full gui refresh now state.notifier->Notify( this, MIDISequencerGUIEvent::GROUP_ALL ); } // return true if we actually found the measure requested return state.cur_measure == measure && state.cur_beat == beat; }
bool MIDISequencer::GetNextEvent( int *tracknum, MIDITimedBigMessage *msg ) { MIDIClockTime t; // ask the iterator for the current event time if( state.iterator.GetCurEventTime(&t) ) { // move current time forward one event MIDIClockTime new_clock; float new_time_ms=0.0f; GetNextEventTime( &new_clock ); GetNextEventTimeMs( &new_time_ms ); // must set cur_clock AFTER GetnextEventTimeMs() is called // since GetNextEventTimeMs() uses cur_clock to calculate state.cur_clock = new_clock; state.cur_time_ms = new_time_ms; // is the next beat marker before this event? if( state.next_beat_time<=t ) { // yes, this is a beat event now. // say this event came on track 0, the conductor track *tracknum = 0; // put current info into beat marker message beat_marker_msg.SetBeatMarker(); beat_marker_msg.SetTime( state.next_beat_time ); *msg = beat_marker_msg; // update our beat count int new_beat = state.cur_beat+1; int new_measure = state.cur_measure; // do we need to update the measure number? if( new_beat>=state.track_state[0]->timesig_numerator ) { // yup new_beat=0; ++new_measure; } // update our next beat time // denom=4 (16) ---> 4/16 midi file beats per symbolic beat // denom=3 (8) ---> 4/8 midi file beats per symbolic beat // denom=2 (4) ---> 4/4 midi file beat per symbolic beat // denom=1 (2) ---> 4/2 midi file beats per symbolic beat // denom=0 (1) ---> 4/1 midi file beats per symbolic beat state.next_beat_time += state.multitrack->GetClksPerBeat() * 4 / (state.track_state[0]->timesig_denominator); state.cur_beat = new_beat; state.cur_measure = new_measure; // now notify the GUI that the beat number changed state.notifier->Notify( this, MIDISequencerGUIEvent( MIDISequencerGUIEvent::GROUP_TRANSPORT, 0, MIDISequencerGUIEvent::GROUP_TRANSPORT_BEAT ) ); // if the new beat number is 0 then the measure changed too if( state.cur_beat==0 ) { state.notifier->Notify( this, MIDISequencerGUIEvent( MIDISequencerGUIEvent::GROUP_TRANSPORT, 0, MIDISequencerGUIEvent::GROUP_TRANSPORT_MEASURE ) ); } // give the beat marker event to the conductor track to process state.track_state[*tracknum]->Process(msg); return true; } else // this event comes before the next beat { MIDITimedBigMessage *msg_ptr; if( state.iterator.GetCurEvent( tracknum, &msg_ptr ) ) { int trk=*tracknum; // copy the event so Process can modify it *msg = *msg_ptr; bool allow_msg=true; // are we in solo mode? if( solo_mode ) { // yes, only allow this message thru if // the track is either track 0 // or it is explicitly solod. if( trk==0 || track_processors[trk]->solo ) { allow_msg=true; } else { allow_msg=false; } } if( !(allow_msg && track_processors[trk]->Process(msg) && state.track_state[trk]->Process(msg)) ) { // the message is not allowed to come out! // erase it msg->SetNoOp(); } // go to the next event on the multitrack state.iterator.GoToNextEvent(); return true; } } } return false; }