void ClipMultiTrack( const MIDIMultiTrack &src, MIDIMultiTrack &dst, double max_time_sec ) { dst.ClearAndResize( src.GetNumTracks() ); dst.SetClksPerBeat( src.GetClksPerBeat() ); double max_event_time = 1000.*max_time_sec; // msec double event_time = 0.; // msec MIDISequencer seq( &src ); seq.GoToTimeMs( 0.f ); if ( !seq.GetNextEventTimeMs ( &event_time ) ) return; // empty src multitrack MIDITimedBigMessage ev; int ev_track; while ( seq.GetNextEvent( &ev_track, &ev ) ) { // ignore NoOp, BeatMarker and other Service messages if ( ev.IsServiceMsg() || ev.IsNoOp() ) continue; dst.GetTrack(ev_track)->PutEvent(ev); if ( event_time >= max_event_time ) break; // end of max_time_sec if ( !seq.GetNextEventTimeMs( &event_time ) ) break; // end of src multitrack } }
void CollapseMultiTrack( const MIDIMultiTrack &src, MIDIMultiTrack &dst ) { dst.ClearAndResize( 1 ); dst.SetClksPerBeat( src.GetClksPerBeat() ); MIDISequencer seq( &src ); seq.GoToZero(); MIDITimedBigMessage ev; int ev_track; while ( seq.GetNextEvent( &ev_track, &ev ) ) { // ignore all src EndOfTrack messages!! if ( ev.IsDataEnd() ) continue; // ignore NoOp, BeatMarker and other Service messages if ( ev.IsServiceMsg() || ev.IsNoOp() ) continue; dst.GetTrack(0)->PutEvent(ev); } // set (single!) dst EndOfTrack message MIDITimedBigMessage end(ev); // copy time of last src event end.SetDataEnd(); dst.GetTrack(0)->PutEvent(end); }
void CopyWithoutChannel( const MIDIMultiTrack &src, MIDIMultiTrack &dst, int ignore_channel ) { dst.ClearAndResize( src.GetNumTracks() ); dst.SetClksPerBeat( src.GetClksPerBeat() ); MIDIClockTime ev_time = 0; MIDISequencer seq( &src ); seq.GoToTime( 0 ); if ( !seq.GetNextEventTime ( &ev_time ) ) return; // empty src multitrack MIDITimedBigMessage ev; int ev_track; while ( seq.GetNextEvent( &ev_track, &ev ) ) { if ( ev.IsServiceMsg() || ev.IsNoOp() ) continue; if ( ev.IsChannelEvent() && ev.GetChannel() == ignore_channel ) continue; dst.GetTrack(ev_track)->PutEvent(ev); } }
bool AddEndingPause( MIDIMultiTrack &tracks, int track_num, MIDIClockTime pause_ticks ) { MIDIClockTime t = tracks.GetTrack( track_num )->GetLastEventTime(); MIDITimedBigMessage msg; msg.SetTime( t + pause_ticks ); // add lowest "note on" in channel 0 with velocity 0 (i.e. "note off") msg.SetNoteOn( 0, 0, 0 ); return tracks.GetTrack( track_num )->PutEvent( msg ); }
int main( int argc, char **argv ) { if ( argc > 1 ) { const char *infile_name = argv[1]; MIDIFileReadStreamFile rs( infile_name ); MIDIMultiTrack tracks; MIDIFileReadMultiTrack track_loader( &tracks ); MIDIFileRead reader( &rs, &track_loader ); // set amount of tracks equal to midifile tracks.ClearAndResize( reader.ReadNumTracks() ); // MIDISequencerGUIEventNotifierText notifier( stdout ); // MIDISequencer seq( &tracks, ¬ifier ); MIDISequencer seq ( &tracks ); // load the midifile into the multitrack object if ( !reader.Parse() ) { cerr << "\nError parse file " << infile_name << endl; return -1; } if ( argc > 2 ) { cout << endl; int mode = atoi ( argv[2] ); if ( mode == 0 ) { DumpMIDIMultiTrack( &tracks ); } else // mode = 1 { PlayDumpSequencer( &seq ); } } // cout << MultiTrackAsText( tracks ); // new util fun double dt = seq.GetMisicDurationInSeconds(); cout << "\nMisic duration = " << dt << endl; } else { cerr << "\nusage:\n jdkmidi_test_sequencer FILE.mid [0 for DumpMIDIMultiTrack]\n"; cerr << " [1 for PlayDumpSequencer]\n"; return -1; } return 0; }
void CompressStartPause( const MIDIMultiTrack &src, MIDIMultiTrack &dst, int ignore_channel ) { dst.ClearAndResize( src.GetNumTracks() ); dst.SetClksPerBeat( src.GetClksPerBeat() ); MIDIClockTime ev_time = 0; MIDISequencer seq( &src ); seq.GoToTime( 0 ); if ( !seq.GetNextEventTime ( &ev_time ) ) return; // empty src multitrack MIDITimedBigMessage ev; int ev_track; bool compress = true; MIDIClockTime old_ev_time = 0, delta_ev_time = 0, ev_time0 = 0; while ( seq.GetNextEvent( &ev_track, &ev ) ) { if ( ev.IsServiceMsg() || ev.IsNoOp() ) continue; if ( ev.IsChannelEvent() && ev.GetChannel() == ignore_channel ) continue; ev_time = ev.GetTime(); if ( compress ) { // compress time intervals between adjacent messages to 1 tick if (ev_time > old_ev_time) ++delta_ev_time; old_ev_time = ev_time; ev.SetTime( delta_ev_time ); if ( ev.ImplicitIsNoteOn() ) { compress = false; ev_time0 = ev_time - delta_ev_time; } } else { ev.SetTime( ev_time - ev_time0 ); } dst.GetTrack(ev_track)->PutEvent(ev); } }
bool ReadMidiFile(const char *file, MIDIMultiTrack &dst) { MIDIFileReadStreamFile rs( file ); MIDIFileReadMultiTrack track_loader( &dst ); MIDIFileRead reader( &rs, &track_loader ); // set amount of dst tracks equal to midifile dst.ClearAndResize( reader.ReadNumTracks() ); // load the midifile into the multitrack object return reader.Parse(); }
bool WriteMidiFile(const MIDIMultiTrack &src, const char *file, bool use_running_status) { MIDIFileWriteStreamFileName out_stream( file ); if ( !out_stream.IsValid() ) return false; MIDIFileWriteMultiTrack writer( &src, &out_stream ); // write midifile with or without running status usage writer.UseRunningStatus( use_running_status ); int tracks_number = src.GetNumTracksWithEvents(); return writer.Write( tracks_number ); }
// delete all text events from multitrack object bool DeleteAllTracksText( MIDIMultiTrack &tracks ) { bool text_deleted = false; int num_tracks = tracks.GetNumTracksWithEvents(); for ( int nt = 0; nt < num_tracks; ++nt ) { MIDITrack &trk = *tracks.GetTrack( nt ); int num_events = trk.GetNumEvents(); for ( int ne = 0; ne < num_events; ++ne ) { MIDITimedBigMessage *msg = trk.GetEvent( ne ); // convert any text midi event to NoOp event if ( msg->IsTextEvent() ) { trk.MakeEventNoOp( ne ); text_deleted = true; } } } return text_deleted; }
void LastEventsProlongation( MIDIMultiTrack &tracks, int track_num, MIDIClockTime add_ticks ) { MIDITrack *track = tracks.GetTrack( track_num ); int index = track->GetNumEvents() - 1; if ( add_ticks == 0 || index < 0 ) return; MIDITimedBigMessage *msg = track->GetEvent( index ); MIDIClockTime tmax = msg->GetTime(); while ( msg->GetTime() == tmax ) { msg->SetTime( tmax + add_ticks ); if ( --index < 0 ) break; msg = track->GetEvent( index ); } }
void SoloMelodyConverter( const MIDIMultiTrack &src, MIDIMultiTrack &dst, int ignore_channel ) { // this simple code works better for src MultiTrack with 1 track, // if not, we can make before the call of CollapseMultiTrack() dst.ClearAndResize( src.GetNumTracks() ); dst.SetClksPerBeat( src.GetClksPerBeat() ); MIDIClockTime ev_time = 0; MIDISequencer seq( &src ); seq.GoToTime( 0 ); if ( !seq.GetNextEventTime ( &ev_time ) ) return; // empty src multitrack MIDITimedBigMessage ev; int ev_track; int solo_note = -1; // highest midi note number in current time, valid values 0...127 bool solo_note_on = false; MIDITimedBigMessage solo_note_on_ev; // last solo note on event solo_note_on_ev.SetNoOp(); while ( seq.GetNextEvent( &ev_track, &ev ) ) { if ( ev.IsServiceMsg() || ev.IsNoOp() ) continue; if ( ev.IsChannelEvent() ) { if ( ev.GetChannel() == ignore_channel ) continue; // if ( ev.IsAllNotesOff() ) ... ; // for future work... if ( ev.IsNote() ) { int new_note = ev.GetNote(); // skip all note events if new note lower than solo note if ( new_note < solo_note ) continue; // else ( new_note >= solo_note ) if ( ev.ImplicitIsNoteOn() ) // new note on event { if ( solo_note_on ) // new note on after previous solo note on { // make noteoff message for previous solo note solo_note_on_ev.SetTime( ev.GetTime() ); solo_note_on_ev.SetVelocity( 0 ); // note off dst.GetTrack(ev_track)->PutEvent( solo_note_on_ev ); // make new solo note solo_note_on_ev = ev; solo_note = new_note; } else // ( solo_note_on == false ) - new note on after previous silence { // make new solo note solo_note_on = true; solo_note_on_ev = ev; solo_note = new_note; } } else // new note off event ( new_note >= solo_note ) { if ( solo_note_on ) // new note off after previous solo note on { if ( new_note == solo_note ) // solo note off event { // test channels of the events if ( ev.GetChannel() == solo_note_on_ev.GetChannel() ) { solo_note_on = false; solo_note = -1; // erase solo_note } else continue; // skip other note off stuff } else // ( new_note > solo_note ) any other note off event continue; // skip other note off stuff } else // ( solo_note_on == false ) - new note off after previous silence continue; // skip other note off stuff } } } dst.GetTrack(ev_track)->PutEvent(ev); } }
void CollapseAndExpandMultiTrack( const MIDIMultiTrack &src, MIDIMultiTrack &dst ) { CollapseMultiTrack(src, dst); dst.AssignEventsToTracks(0); }
int main ( int argc, char **argv ) { int retcode = -1; if ( argc <= 2 ) { args_err(); return retcode; } const char *infile = argv[1]; const char *outfile = argv[2]; int mode = 0; if ( argc > 3 ) mode = abs ( atol( argv[3] ) ); MIDIMultiTrack tracks, tracks2; if ( !ReadMidiFile( infile, tracks ) ) { cerr << "\nError reading file " << infile << endl; return retcode; } if ( mode%2 == 1 ) // need to reduce outfile size { // delete all text events from all tracks if ( DeleteAllTracksText( tracks ) ) { cout << "\nAll midi text events deleted." << endl; } if ( tracks.GetNumTracksWithEvents() == 1 ) { // remake multitrack object and optimize new tracks content: // move all channal events to tracks 1-16, and all other types of events to track 0 // and reduce midifile size because of increase number of events with running status tracks.AssignEventsToTracks( 0 ); cout << "\nAll midi channal events moved to tracks 1-16." << endl; } } MIDIMultiTrack *outtracks = &tracks; if ( mode >= 2 ) // need to delete start pause { CompressStartPause( tracks, tracks2 ); outtracks = &tracks2; cout << "\nStart pause deleted (decreased)." << endl; } if ( WriteMidiFile( *outtracks, outfile) ) { int num_tracks = outtracks->GetNumTracksWithEvents(); cout << "\nAll OK. Number of tracks with events " << num_tracks << endl; retcode = 0; } else { cerr << "\nError writing file " << outfile << endl; } return retcode; }