A very basic, and not comfortable, command line step sequencer, made for demonstrating editing capabilities of the NiCMidi library. It creates an AdvancedSequencer class instance, gets it MultiTrack, and allows the user to edit it. You can load and save MIDI files, play them, view the file content, edit the file. You can insert, delete or change these MIDI events: note, control (in particular volume and pan), program and tempo. For changing an event, insert a new event (same note, control, program, tempo) at same time position.
Requires functions.cpp, which contains command line I/O functions.
#include "test_stepsequencer.h"
#include "functions.h"
#include "../include/advancedsequencer.h"
#include "../include/filewritemultitrack.h"
#include <iostream>
using namespace std;
extern string command, par1, par2, par3;
position cur_pos(multitrack);
edit_block cur_block;
MIDIEditMultiTrack edit_multitrack;
char filename[200];
unsigned char NameToValue(string s) {
static const unsigned char noteoffsets[7] = { 9, 11, 0, 2, 4, 5, 7,};
int p = s.find_first_not_of(" ");
char ch = tolower(s[p]);
unsigned char note;
unsigned char octave;
if (string("abcdefg").find(ch) == string::npos)
return 0;
note = noteoffsets[ch - 'a'];
p = s.find_first_not_of(" ", p + 1);
if( s[p] == '#') {
note++;
p = s.find_first_not_of(" ", p+1);
}
else if (s[p] == 'b') {
note--;
p = s.find_first_not_of(" ", p + 1);
}
if (string("0123456789").find(s[p]) == string::npos)
return 0;
octave = s[p] - '0';
return 12 * octave + note;
}
void PrintResolution() {
cout <<
"MultiTrack resolution is " << multitrack->
GetClksPerBeat() <<
" clocks per beat" << endl;
cout << "Current step size is " << cur_pos.getstep() << " clocks" << endl;
}
void PrintCurrentStatus() {
cout << "*** Current cursor pos: Track: " << cur_pos.gettrack() << " Time: " << cur_pos.gettime() << " (" <<
sequencer.GetCurrentMeasure() + 1 <<":" << sequencer.GetCurrentBeat() + 1;
if (sequencer.GetCurrentBeatOffset() > 0)
cout << ":" << sequencer.GetCurrentBeatOffset();
cout << ")" << endl;
}
int main(int argc, char **argv) {
int last_note_vel = 100;
int event_num;
*filename = 0;
cout << "Step sequencer example for NiCMidi library" << endl <<
"Copyright 2014 - 2021 Nicola Cassetta" << endl << endl;
PrintResolution();
cout << endl << "TYPE help TO GET A LIST OF AVAILABLE COMMANDS" << endl << endl;
while (command != "quit") {
PrintCurrentStatus();
GetCommand();
if(command == "")
continue;
if(command == "load") {
if (sequencer.Load(par1.c_str())) {
cout << "Loaded file " << par1 << endl;
PrintResolution();
strcpy (filename, par1.c_str());
}
else
cout << "Error loading file" << endl;
}
else if(command == "save") {
if (par1.size() > 0)
strcpy (filename, par1.c_str());
if (strlen(filename) > 0) {
cout << "File saved" << endl;
else
cout << "Error writing file" << endl;
}
else
cout << "File name not defined" << endl;
}
else if (command == "play") {
if (sequencer.IsLoaded())
sequencer.Play();
else
cout << "No content in the multitrack!" << endl;
}
else if (command == "stop")
sequencer.Stop();
else if (command == "rew") {
cur_pos.rewind();
sequencer.GoToZero();
}
else if (command == "goto") {
int meas = atoi(par1.c_str()) - 1;
int beat = (par2.length() == 0 ? 0 : atoi(par2.c_str()) - 1);
if (sequencer.GoToMeasure(meas, beat))
cur_pos.settime(sequencer.GetCurrentMIDIClockTime());
else
cout << "Invalid position" << endl;
}
else if (command == "dump") {
if (par1.size() == 0)
DumpMIDIMultiTrackWithPauses(multitrack);
else {
int trk_num = atoi(par1.c_str());
MIDITrack* trk = sequencer.GetTrack(trk_num);
DumpMIDITrackWithPauses(trk, trk_num);
}
else
cout << "Invalid track number" << endl;
}
}
else if (command == "notify") {
if (par1 == "on")
else if (par1 == "off")
else
cout << "You must specify on or off" << endl;
}
else if (command == "<") {
int steps = (par1.length() == 0 ? 1 : atoi(par1.c_str()));
for (int i = 0; i < steps; i++)
cur_pos.stepback();
sequencer.GoToTime(cur_pos.gettime());
}
else if (command == ">") {
int steps = (par1.length() == 0 ? 1 : atoi( par1.c_str()));
for (int i = 0; i < steps; i++)
cur_pos.stepforward();
sequencer.UpdateStatus();
}
sequencer.GoToTime(cur_time);
}
else if (command == "t<") {
cur_pos.previoustrack();
trk = multitrack->
GetTrack(cur_pos.gettrack());
}
else if (command == "t>") {
cur_pos.nexttrack();
trk = multitrack->
GetTrack(cur_pos.gettrack());
}
else if (command == "step") {
cur_pos.setstep(atoi(par1.c_str()));
PrintResolution();
}
else if (command == "note") {
if (par2 != "*") {
MIDIClockTime len = (par2.length() == 0 ? last_note_length : atoi(par2.c_str()));
int vel = (par3.length() == 0 ? last_note_vel : atoi(par3.c_str()));
msg.
SetNoteOn(cur_pos.gettrack() - 1, NameToValue(par1), vel);
}
else {
msg.
SetNoteOn(cur_pos.gettrack() - 1, NameToValue(par1), 100);
cout << "Event not found" << endl;
else {
}
}
sequencer.UpdateStatus();
}
else if (command == "volume") {
if (par1 != "*") {
}
else {
cout << "Event not found" << endl;
else {
}
}
sequencer.UpdateStatus();
}
else if ( command == "pan") {
if (par1 != "*") {
}
else {
cout << "Event not found" << endl;
else {
}
}
sequencer.UpdateStatus();
}
else if (command == "control") {
if (par2 != "*") {
msg.
SetControlChange(cur_pos.gettrack() - 1, atoi(par1.c_str()), atoi(par2.c_str()));
}
else {
cout << "Event not found" << endl;
else {
}
}
sequencer.UpdateStatus();
}
else if (command == "program") {
if (par1 != "*") {
}
else {
cout << "Event not found" << endl;
else {
}
}
sequencer.UpdateStatus();
}
else if (command == "tempo") {
if (par1 != "*") {
}
else {
cout << "Event not found" << endl;
else {
}
}
sequencer.UpdateStatus();
}
else if (command == "time") {
if (par1 != "*") {
msg.
SetTimeSig(atoi(par1.c_str()), atoi(par2.c_str()));
}
else {
cout << "Event not found" << endl;
else {
}
}
sequencer.UpdateStatus();
}
else if (command == "bb") {
cur_block.time_begin = cur_block.time_end = cur_pos.gettime();
if (par1.size() == 0) {
cur_block.track_begin = 0;
}
else
cur_block.track_begin = cur_block.track_end = atoi(par1.c_str());
}
else if (command == "be") {
if (cur_pos.gettime() < cur_block.time_begin)
cout << "Selection error" << endl;
else {
cur_block.time_end = cur_pos.gettime();
cout << "Block selected: from " << cur_block.time_begin << " to " <<
cur_block.time_end << "; tracks " << cur_block.track_begin <<
"-" << cur_block.track_end << endl;
}
}
else if (command == "bcopy") {
multitrack->
EditCopy(cur_block.time_begin, cur_block.time_end,
cur_block.track_begin, cur_block.track_end, &edit_multitrack);
sequencer.UpdateStatus();
}
else if (command == "bclear") {
multitrack->
EditClear(cur_block.time_begin, cur_block.time_end,
cur_block.track_begin, cur_block.track_end);
sequencer.UpdateStatus();
}
else if (command == "bcut") {
if (cur_block.track_begin != 0 && cur_block.track_end != multitrack->
GetNumTracks() + 1)
cout << "You can't cut a number of tracks lesser than actual number" << endl;
else {
multitrack->
EditCut(cur_block.time_begin, cur_block.time_end, 0);
sequencer.UpdateStatus();
}
}
else if (command == "bpaste") {
unsigned int tr_start = atoi(par1.c_str());
if (par2.size() == 0) {
multitrack->
EditInsert(cur_pos.gettime(), tr_start, 1, &edit_multitrack);
sequencer.UpdateStatus();
}
else if (par2 == "o") {
multitrack->
EditReplace(cur_pos.gettime(), tr_start, 1,
true, &edit_multitrack);
sequencer.UpdateStatus();
}
else
cout << "You must indicate initial track" << endl;
}
else if (command == "help") {
cout << helpstring1;
cout << "Press ENTER to continue";
getchar();
cout << helpstring2;
cout << "Press ENTER to continue";
getchar();
cout << helpstring3;
}
else if (command != "quit")
cout << "Unrecognized command" << endl;
}
return EXIT_SUCCESS;
}
An enhanced MIDISequencer, able to directly load and play MIDI files and many more.
Definition: advancedsequencer.h:93
void SetTimeSig(unsigned char num, unsigned char den, unsigned char clocks_per_metronome=0, unsigned char num_32_per_quarter=8)
Makes the message a time signature meta-message with given time (numerator and denominator).
void SetTempo(float tempo_bpm)
Makes the message a tempo change meta-message with given tempo (in bpm).
void SetControlChange(unsigned char chan, unsigned char ctrl, unsigned char val)
Makes the message a control change message with given channel, controller and value.
void SetProgramChange(unsigned char chan, unsigned char prog)
Makes the message a program change message with given channel and program.
void SetNoteOn(unsigned char chan, unsigned char note, unsigned char vel)
Makes the message a note on message with given channel, note and velocity.
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
unsigned int GetClksPerBeat() const
Returns the MIDI clocks per beat of all tracks (i.e. the number of MIDI ticks in a quarter note).
Definition: multitrack.h:76
unsigned int GetNumTracks() const
Returns the number of allocated tracks.
Definition: multitrack.h:87
MIDIClockTime GetEndTime() const
Returns the end time of the longest track.
bool SetEndTime(MIDIClockTime end_time)
Sets the time of the data end event to end_time.
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
The MIDITimedMessage class inherits from the MIDIMessage and represents a message associated with a s...
Definition: msg.h:382
void SetTime(MIDIClockTime t)
Sets the MIDIClockTime associated with the message.
Definition: msg.h:410
Manages a std::vector of MIDITimedMessage objects storing MIDI events, with methods for editing them.
Definition: track.h:86
bool InsertEvent(const MIDITimedMessage &msg, tInsMode mode=INSMODE_DEFAULT)
Inserts a single event into the track.
bool DeleteEvent(const MIDITimedMessage &msg)
Deletes an event from the track.
MIDITimedMessage & GetEvent(unsigned int ev_num)
Returns a reference to an event in the track.
Definition: track.h:149
bool InsertNote(const MIDITimedMessage &msg, MIDIClockTime len, tInsMode mode=INSMODE_DEFAULT)
Inserts a Note On and a Note Off event into the track.
bool DeleteNote(const MIDITimedMessage &msg)
Deletes a Note On and corresponding Note Off events from the track.
bool FindEventNumber(const MIDITimedMessage &msg, int *event_num, int mode=COMPMODE_EQUAL) const
Finds an event in the track matching a given event.
unsigned long MIDIClockTime
The type of a variable which can hold a time in MIDI ticks.
Definition: midi.h:40
bool WriteMIDIFile(const char *filename, int format, const MIDIMultiTrack *tracks, bool strip=false)
Writes the given MIDIMultiTrack object into a MIDI file.
@ COMPMODE_SAMEKIND
the method searches for an event matching the MIDITimedMessage::IsSameKind() method.
Definition: track.h:67
@ C_MAIN_VOLUME
main volume control
Definition: midi.h:143
@ C_PAN
panpot stereo control
Definition: midi.h:145