NiCMidi 1.1.0
A MIDI library derived from J.D.Koftinoff jdksmidi
test_sequencer.cpp

A command line example of the features of the MIDISequencer class. It creates an instance of the class and allows the user to interact with it. You can load MIDI files and play them changing some parameters (it has more limited features than the AdvancedSequencer).

Requires functions.cpp, which contains command line I/O functions.

/*
* Example file for NiCMidi - A C++ Class Library for MIDI
*
* Copyright (C) 2021, 2022 Nicola Cassetta
* https://github.com/ncassetta/NiCMidi
*
* This file is part of NiCMidi.
*
* NiCMidi is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* NiCMidi is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with NiCMidi. If not, see <http://www.gnu.org/licenses/>.
*/
/*
A command line example of the features of the MIDISequencer
class. It creates an instance of the class and allows the user
to interact with it. You can load MIDI files and play them
changing some parameters (it has more limited features than the
AdvancedSequencer).
Requires functions.cpp, which contains command line I/O functions.
*/
#include "../include/sequencer.h"
#include "../include/manager.h"
#include "../include/notifier.h"
#include "../include/filereadmultitrack.h" // for LoadMIDIFile()
#include "functions.h" // helper functions for input parsing and output
using namespace std;
// G L O B A L S //
// a MIDIMultiTrack
MIDIMultiTrack multitrack;
// a text notifier
// this constructor creates the sequencer from the given multitrack
MIDISequencer sequencer(&multitrack, &notifier);
extern string command, par1, par2; // used by GetCommand() for parsing the user input
const char helpstring[] = // shown by the help command
"\nAvailable commands:\n\
load filename : Loads the file into the sequencer\n\
ports : Enumerates MIDI In and OUT ports\n\
play : Starts playback from current time\n\
loop meas1 meas2 : Sets loop play (doesn't start it!) from\n\
meas1 to meas2. Give 0 0 for non loop play\n\
stop : Stops playback\n\
rew : Goes to the beginning (stops the playback)\n\
goto meas [beat] : Moves current time to given meas and beat\n\
(numbered from 0)\n\
playmode u/b : Sets the play mode (bounded or unbounded)\n\
dump [trk] : Prints a dump of all midi events in the file\n\
(or in the track trk)\n\
outport track port : Sets the MIDI port for the given track\n\
tscale scale : Sets global tempo scale. scale is in percent\n\
(ex. 200 = twice faster, 50 = twice slower)\n\
tshift track amt : Sets the time shift for given track. The amount can\n\
be positive or negative.\n\
trackinfo [v] : Shows info about all tracks of the file. If you\n\
add the v the info are more complete.\n\
notify on/off : Turns on and off the notifier\n\
b : (backward) Moves current time to the previous measure\n\
f : (forward) Moves current time to the next measure\n\
help : Prints this help screen\n\
quit : Exits\n\
All commands can be given during playback\n";
// M A I N //
int main(int argc, char **argv) {
notifier.SetEnable(false);
cout << "TYPE help TO GET A LIST OF AVAILABLE COMMANDS" << endl << endl;
while (command != "quit") { // main loop
GetCommand(); // gets user input and splits it into command, par1, par2
if(command == "") // empty command
continue;
if (command == "load") { // loads a file
// this is different from the same on AdvancedSequencer, because there is
// no dedicated method in MIDISequencer: you must load the multitrack and
// then call MIDISequencer::Reset() for initializing the sequencer
if (LoadMIDIFile(par1, &multitrack))
cout << "Loaded file " << par1 << endl;
else
cout << "Error loading file" << endl;
sequencer.Reset();
}
else if (command == "ports") { // enumerates the midi ports
cout << "MIDI IN PORTS:" << endl;
for (unsigned int i = 0; i < MIDIManager::GetNumMIDIIns(); i++)
cout << i << ": " << MIDIManager::GetMIDIInName(i) << endl;
}
else
cout << "NO MIDI IN PORTS" << endl;
cout << "MIDI OUT PORTS:" << endl;
for (unsigned int i = 0; i < MIDIManager::GetNumMIDIOuts(); i++)
cout << i << ": " << MIDIManager::GetMIDIOutName(i) << endl;
}
else
cout << "NO MIDI OUT PORTS" << endl;
}
else if (command == "play") { // starts playback
sequencer.Play();
cout << "Sequencer started at measure: " << sequencer.GetCurrentMeasure() << ":"
<< sequencer.GetCurrentBeat() << endl;
}
else if (command == "loop") { // sets repeated play
int beg = atoi(par1.c_str());
int end = atoi(par2.c_str());
if (!(beg == 0 && end == 0)) {
if (sequencer.SetRepeatPlay(true, beg, end))
cout << "Repeat play set from measure " << beg << " to measure " << end << endl;
else
cout << "Invalid parameters: repeat play cleared" << endl;
}
else {
sequencer.SetRepeatPlay(false, 0, 0);
cout << "Repeat play cleared" << endl;
}
}
else if (command == "stop") { // stops playback
sequencer.Stop();
cout << "Sequencer stopped at measure: " << sequencer.GetCurrentMeasure() << ":"
<< sequencer.GetCurrentBeat() << endl;
}
else if (command == "rew") { // stops and rewind to time 0
sequencer.Stop();
sequencer.GoToZero();
cout << "Rewind to 0:0" << endl;
}
else if (command == "goto") { // goes to meas and beat
// if there are program change, control change or sysex events between actual
// and new time, MIDISequencer doesn't send them, so you can expect inaccurate
// playing when you jump from one time to another (this is solver in
// AdvancedSequencer)
int meas = atoi(par1.c_str());
int beat = atoi (par2.c_str());
if (sequencer.GoToMeasure(meas, beat))
cout << "Actual position: " << sequencer.GetCurrentMeasure() << ":"
<< sequencer.GetCurrentBeat() << endl;
else
cout << "Invalid position" << endl;
}
else if (command == "playmode") {
if (par1 == "u") {
sequencer.SetPlayMode(MIDISequencer::PLAY_UNBOUNDED);
cout << "Set sequencer play mode to UNBOUNDED" << endl;
}
else if (par1 == "b") {
sequencer.SetPlayMode(MIDISequencer::PLAY_BOUNDED);
cout << "Set sequencer play mode to BOUNDED" << endl;
}
else
cout << "Invalid parameter" << endl;
}
else if (command == "dump") { // prints a dump of the sequencer contents
if (par1.size() == 0)
DumpMIDIMultiTrackWithPauses(&multitrack);
else {
int trk_num = atoi(par1.c_str());
if (multitrack.IsValidTrackNumber(trk_num)) {
MIDITrack* trk = multitrack.GetTrack(trk_num);
DumpMIDITrackWithPauses(trk, trk_num);
}
else
cout << "Invalid track number" << endl;
}
}
else if (command == "outport") { // changes the midi out port
int trk_num = atoi(par1.c_str());
int port = atoi(par2.c_str());
if (sequencer.SetTrackOutPort(trk_num, port))
cout << "Assigned out port n. " << sequencer.GetTrackOutPort(trk_num)
<< " to track " << trk_num << endl;
else
cout << "Invalid parameters" << endl;
}
else if (command == "tscale") { // scales playback tempo
int scale = atoi(par1.c_str());
sequencer.SetTempoScale(scale);
cout << "Tempo scale : " << scale << "% " <<
" Effective tempo: " << sequencer.GetTempoWithScale() << " bpm" << endl;
}
else if (command == "tshift") { // sets the time shift (in ticks) of a track
int trk_num = atoi(par1.c_str());
int amount = atoi(par2.c_str());
sequencer.SetTrackTimeShift(trk_num, amount);
cout << "Track " << trk_num << " time shifted by " << amount << " MIDI ticks" << endl;
}
else if (command == "trackinfo") { // prints info about tracks
bool verbose = (par1 == "v");
DumpAllTracksAttr(&multitrack, verbose);
}
else if (command == "notify") {
if (par1 == "on") {
notifier.SetEnable(true);
cout << "Notifier on" << endl;
}
else if (par1 == "off") {
notifier.SetEnable(false);
cout << "Notifier off" << endl;
}
}
else if (command == "b") { // goes a measure backward
int meas = sequencer.GetCurrentMeasure();
if (sequencer.GoToMeasure(--meas))
cout << "Actual position: " << sequencer.GetCurrentMeasure() << ":"
<< sequencer.GetCurrentBeat() << endl;
}
else if (command == "f") { // goes a measure forward
int meas = sequencer.GetCurrentMeasure();
if (sequencer.GoToMeasure(++meas))
cout << "Actual position: " << sequencer.GetCurrentMeasure() << ":"
<< sequencer.GetCurrentBeat() << endl;
}
else if (command == "help") // prints help screen
cout << helpstring;
else if (command != "quit")
cout << "Unrecognized command" << endl;
}
return EXIT_SUCCESS;
}
static unsigned int GetNumMIDIOuts()
Returns the number of MIDI out ports in the system.
static const std::string & GetMIDIInName(unsigned int n)
Returns the system name of the given MIDI in port.
static unsigned int GetNumMIDIIns()
Returns the number of MIDI in ports in the system.
static const std::string & GetMIDIOutName(unsigned int n)
Returns the system name of the given MIDI out port.
Holds an array of pointers to MIDITrack objects to be played simultaneously.
Definition: multitrack.h:50
bool IsValidTrackNumber(unsigned int trk_num) const
Returns true if trk_num is in thee range 0 ... GetNumTracks() - 1.
Definition: multitrack.h:96
MIDITrack * GetTrack(unsigned int trk_num)
Returns the pointer to the track.
Definition: multitrack.h:79
virtual void SetEnable(bool f)
Sets message sending on/off.
Definition: notifier.h:179
A MIDISequencerGUINotifier which sends text messages to a std::ostream (std::cout as default).
Definition: notifier.h:191
A MIDITickComponent which implements a basic sequencer, able to play the MIDI events contained in a M...
Definition: sequencer.h:193
@ PLAY_BOUNDED
See SetPlayMode()
Definition: sequencer.h:478
@ PLAY_UNBOUNDED
See SetPlayMode()
Definition: sequencer.h:479
Manages a std::vector of MIDITimedMessage objects storing MIDI events, with methods for editing them.
Definition: track.h:86
bool LoadMIDIFile(const char *filename, MIDIMultiTrack *tracks, MIDIFileHeader *const head=0)
Loads a MIDI file into a MIDIMultiTrack object.